为什么会是重大推荐呢?是因为公司线上出现了OOM!!!!
细思极恐
为啥呢?是因为我们在做导入的时候经常会面临两个问题
1、我们导入模版一般只是读取表格的第一个sheet,但是第一个sheet数据不多,但是导入的表格可能会非常大。
这有可能是因为存在其他的sheet,毕竟用户方我们控制不了。
2、虽然导入只有一个sheet,且数据看起来很少,但是代码读取却消耗了很大内存
这有可能是因为sheet数据的下面看起来是空的地方,存在大量的空格、有格式的空数据等。
目前发现的我们系统就是因为这两个问题,直接导致系统OOM。
重点分析报错之后,得出来一个结论:
划重点:读取excel的opi的方法大量消耗内存!!!
我们采用EasyExcal替换poi的读取方法
github源码如下:https://github.com/alibaba/easyexcel
EasyExcal详细的不再赘述,可以看源码,上面有很多的测试方法实例。
相关内存分析:https://blog.csdn.net/qq_34129814/article/details/118121974
EasyExcal和poi读取表格数据-简单的内存分析
代码取自项目里面的test方法,所以EasyExcal结合自己的系统的还是请看源码
public class EasyExcelUtil {
private static Logger logger = LoggerFactory.getLogger(EasyExcelUtil.class);
//这里本人方法是从文件服务器上的文件下载并读成了byte
public static void getDataList(byte[] bytes){
try {
// File file = new File("/Users/sunminghao/Downloads/过滤器.xlsx");
// List<Object> list = EasyExcel.read(file).sheet().headRowNumber(0).doReadSync();
InputStream inputStream = new ByteArrayInputStream(bytes);
//使用easyExcal直接读取sheet数据为一个list,其中包含的有标题信息
List<Object> list = EasyExcel.read(inputStream).sheet().headRowNumber(0).doReadSync();
logger.info("数据:{}", list.size());
logger.info("easyExcal 读取list 占用内存大小 = " + ObjectSizeCalculator.getObjectSize(list));
//搞个测试方法(poi读取表格数据)
poiTest(bytes);
//因为老代码是poi的sheet做的,为了尽可能的少改代码,顺便测一下转sheet
getSheetByList(list);
}catch (Exception e){
logger.error("EasyExcelUtil getDataList is error", e);
}
}
public static void poiTest(byte[] bytes) throws IOException, InvalidFormatException {
Workbook workbook = WorkbookFactory.create(new ByteArrayInputStream(bytes));
Sheet sheet = workbook.getSheetAt(0);
logger.info("poi sheet getLastRowNum = " + sheet.getLastRowNum());
logger.info("poi sheet workbook 占用内存大小 = " + ObjectSizeCalculator.getObjectSize(workbook));
logger.info("poi sheet sheet 占用内存大小 = " + ObjectSizeCalculator.getObjectSize(sheet));
}
public static void getSheetByList(List<Object> list){
HSSFWorkbook workbook = new HSSFWorkbook();
HSSFSheet sheet = workbook.createSheet("用户信息");
if(list != null && list.size()>0) {
Object firstObject = list.get(0);
HSSFRow head = sheet.createRow(0);
String str1= JSONObject.toJSON(firstObject).toString();
HashMap hashMap1 = JSON.parseObject(str1, HashMap.class);
for (int i=0;i<hashMap1.size();i++){
head.createCell(i).setCellValue(hashMap1.get(String.valueOf(i)).toString());
}
list.remove(0);
for (Object object : list) {
HSSFRow body = sheet.createRow(sheet.getLastRowNum()+1);
String str= JSONObject.toJSON(object).toString();
HashMap hashMap = JSON.parseObject(str, HashMap.class);
for (int i=0;i<hashMap.size();i++){
body.createCell(i).setCellValue(hashMap.get(String.valueOf(i)) == null ? "" : hashMap.get(String.valueOf(i)).toString());
}
}
}
logger.info("将easyExcal读取的list转成poi的sheet 占用内存大小 = " + ObjectSizeCalculator.getObjectSize(sheet));
}
}
运行结果:(被整理过,截图不太好 哈哈哈)
数据:920
easyExcal 读取list 占用内存大小 = 620520
poi sheet getLastRowNum = 919
poi sheet workbook 占用内存大小 = 69408488
poi sheet sheet 占用内存大小 = 69450176
将easyExcal读取的list转成poi的sheet 占用内存大小 = 1222856
结果很明显嘛
1、easyExcal搞出来的数据占用的内存很小嘛(毕竟我们主要取第一个sheet)
详细的可以看方法源码,poi的读取方法里面进行的大量的对象转换赋值判断等等,使内存消耗非常的大。而easyExcal则是大刀阔斧的处理。
2、我们再将list转化成为sheet的时候,其实内存直接就进行了翻倍
这里我们是人工处理,当然easyExcal里面也有poi的sheet处理,但是都是不推荐使用的,这里我们手写一个简单的更直观
遗留问题!!
为什么我们读取到的workbook对象占用内存 竟然和 sheet内存差不多!
甚至还有多个sheet的workbook内存竟然还没有一个sheet的内存大?????
Why?
有待下次分析