小G最近搞一個導出excel項目,但是數據量比較多導出來tomcat內存狂飆,開始使用xssf內存狂升,後來使用sax還好點,但是
行數到5萬左右,就2G,
第一步驟定位:liunx上面使用jmap -histo |head -30
可以查到使用內存最多是C和1,2,c是char[] ,1,2爲xml的解析,那就可以看到時poi在解析過程中使用
第二步,找到問題後對問題進行分析
分析結果如下:
自測過程中數據問題:
解析方法 | excel行數 | 內存情況 |
---|---|---|
SXSSFWorkbook | 5萬行 | 2-4G內存 |
XSSFWorkbook | 幾千行 | G級別反正內存消耗很大 |
第三步解決思路:
1、新增多個sheetname效果雖然好點,但是內存還是不怎麼樣在執行到10萬左右,該方不可行;
2、分多個文件,5萬-6萬到2G,應該可以;
3、調用gc進行垃圾回收;
4、加鎖共享鎖lock,因爲執行多個的話,就會2G的倍數,所以加一個鎖,針對服務器加鎖,其實在讀取的時候內存比較大,但是這個是瞬間的,(如果數據不超過1萬條,是可以沒有問題,不用加鎖),這樣就可以同時執行多個文件進行導出操作
5,讀取的時候使用sax進行讀取數據
public static Lock lockPOItool = new ReentrantLock();// 鎖對象
public static void poi(){
Boolean isLockBoolean =false;
if(lockPOItool.tryLock(5l, TimeUnit.MINUTES){
isLockBoolean=true;
//獲取鎖成功
}
XSSFWorkbook xssfworkbook = null;
SXSSFWorkbook workbook = null;
SXSSFSheet sheet = null;
sheetName = null ;
fileName = null;
sheetName="根據業務去劃分生成多excel";
fileName=sheelNameCount.toString()+fileName;
FileInputStream fileInputStream =new FileInputStream(path);//原來的地址
SXSSFCell cell=null;
XSSFSheet xssheet=null;
POIFSFileSystem pfs =null;
try{
//如果有文件說明以前新建過直接進行追加
Boolean isFrist = new File(path+ fileName).exists();
if(i!sFrist){
workbook = new SXSSFWorkbook();
sheet = workbook.createSheet(sheetName);
SXSSFRow row = sheet.createRow((short) 0);
cell = row.createCell((short) 0);
//如果有標題創建標題
rowNum = sheet.getLastRowNum() + 1;//有標題+1從第二行進行
}else{
//追加操作的
pfs = new POIFSFileSystem(fileInputStream);
EncryptionInfo encInfo = new EncryptionInfo(pfs);
Decryptor decryptor = Decryptor.getInstance(encInfo);
decryptor.verifyPassword(pwd);//讀取加密的這個是追加的方法 xssf解密,然後讀取到sxssf裏面
InputStream pfsInputStream=decryptor.getDataStream(pfs);
xssfworkbook = new XSSFWorkbook(pfsInputStream);
pfsInputStream.close();
pfs.close();
xssheet = xssfworkbook.getSheet(sheetName);
rowNum = xssheet.getLastRowNum() + 1;
workbook = new SXSSFWorkbook(xssfworkbook,-1);//-1就是不在使用內存
sheet=workbook.getSheet(sheetName);
}
//
while(rowNum<50){
//數據的存放
SXSSFRow row = sheet.createRow(rowNum);
row.createCell(0).setCellValue(“”“”);//
row.createCell(1).setCellValue(“”“”);//
//官網說這個在(xssfworkbook,-1);有內存中使用,反正這個地方使用也可以
((SXSSFSheet)sheet).flushRows(dataModels.size()-dataModelsInde);
rowNum++;
}
FileOutputStream outBuildE=null;
try{
outBuildE = new FileOutputStream(path + filename);
outBuildE.flush();
workbook.write(outBuildE);
outBuildE.close();
workbook.dispose();
}catch(Exception e){
}
xssfworkbook.close();
workbook.dispose();
}catch(Exception e){
}finally{
workbook=null;
xssfworkbook=null;
sheet=null;
cell=null;
xssheet=null;
pfs=null;
gcPoi();//緩存
if(isLockBoolean){
lockPOItool.unlock();
}catch(Exception e){
}
}
//手動清空下緩存
private void gcPoi(String msg) {
try {
Runtime.getRuntime().gc();
}catch(Exception e){
}
}
官方的接口如下3.8大家可以看下
public static void main(String[] args) throws Throwable {
SXSSFWorkbook wb = new SXSSFWorkbook(100); // keep 100 rows in memory, exceeding rows will be flushed to disk
Sheet sh = wb.createSheet();
for(int rownum = 0; rownum < 1000; rownum++){
Row row = sh.createRow(rownum);
for(int cellnum = 0; cellnum < 10; cellnum++){
Cell cell = row.createCell(cellnum);
String address = new CellReference(cell).formatAsString();
cell.setCellValue(address);
}
}
// Rows with rownum < 900 are flushed and not accessible
for(int rownum = 0; rownum < 900; rownum++){
Assert.assertNull(sh.getRow(rownum));
}
// ther last 100 rows are still in memory
for(int rownum = 900; rownum < 1000; rownum++){
Assert.assertNotNull(sh.getRow(rownum));
}
FileOutputStream out = new FileOutputStream("/temp/sxssf.xlsx");
wb.write(out);
out.close();
// dispose of temporary files backing this workbook on disk
wb.dispose();
}
}
官方如下:
public static void main(String[] args) throws Throwable {
SXSSFWorkbook wb = new SXSSFWorkbook(-1); // turn off auto-flushing and accumulate all rows in memory
Sheet sh = wb.createSheet();
for(int rownum = 0; rownum < 1000; rownum++){
Row row = sh.createRow(rownum);
for(int cellnum = 0; cellnum < 10; cellnum++){
Cell cell = row.createCell(cellnum);
String address = new CellReference(cell).formatAsString();
cell.setCellValue(address);
}
// manually control how rows are flushed to disk
if(rownum % 100 == 0) {
((SXSSFSheet)sh).flushRows(100); // retain 100 last rows and flush all others
// ((SXSSFSheet)sh).flushRows() is a shortcut for ((SXSSFSheet)sh).flushRows(0),
// this method flushes all rows
}
}
FileOutputStream out = new FileOutputStream("/temp/sxssf.xlsx");
wb.write(out);
out.close();
// dispose of temporary files backing this workbook on disk
wb.dispose();
}