最近生产环境有个老项目一直内存报警,不时的还出现内存泄漏,导致需要重启服务器,已经严重影响正常服务了。获取生成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 }
事件模式下: