最近生產環境有個老項目一直內存報警,不時的還出現內存泄漏,導致需要重啟服務器,已經嚴重影響正常服務了。獲取生成dump文件后,使用MAT工具進行分析,發現是其中有個Excel文件上傳功能時,經常會導致內存溢出。原因是:POI在加載Excel引發了內存泄漏,中間創建了大量的對象,占用了大量的內存。查看代碼POI讀取Excel的方式發現使用的是用戶模式。
POI提供了2中讀取Excel的模式,分別是:
用戶模式:也就是poi下的usermodel有關包,它對用戶友好,有統一的接口在ss包下,但是它是把整個文件讀取到內存中的,對於大量數據很容易內存溢出,所以只能用來處理相對較小量的數據;
事件模式:在poi下的eventusermodel包下,相對來說實現比較復雜,但是它處理速度快,占用內存少,可以用來處理海量的Excel數據。
以下便針對兩種模式,進行處理,分析兩種模式下解析同一excel文件,然后觀察內存波動情況。
用戶模式下:
1 package excel; 2 3 import java.io.File; 4 import java.io.FileInputStream; 5 import java.io.IOException; 6 import java.io.InputStream; 7 import org.apache.poi.ss.usermodel.Cell; 8 import org.apache.poi.ss.usermodel.Row; 9 import org.apache.poi.ss.usermodel.Sheet; 10 import org.apache.poi.ss.usermodel.Workbook; 11 import org.apache.poi.xssf.usermodel.XSSFWorkbook; 12 13 public class UserModel { 14 15 public static void main(String[] args) throws InterruptedException { 16 try { 17 Thread.sleep(5000); 18 System.out.println("read---start---!"); 19 for (int i = 0; i < 100; i++) { 20 try { 21 Workbook wb = null; 22 File file = new File("E:/cocoxu/test_mode/excelTest.xlsx"); 23 InputStream fis = new FileInputStream(file); 24 wb = new XSSFWorkbook(fis); 25 Sheet sheet = wb.getSheetAt(0); 26 for (Row row : sheet) { 27 for (Cell cell : row) { 28 System.out.println("row:" + row.getRowNum() + ",cell:" + cell.toString()); 29 } 30 } 31 } catch (IOException e) { 32 e.printStackTrace(); 33 } 34 } 35 System.out.println("read--end--!"); 36 Thread.sleep(1000); 37 } catch (Exception e) { 38 e.printStackTrace(); 39 } 40 } 41 }
事件模式下: