点击上方关注 “终端研发部

设为“星标”,和你一起掌握更多数据库知识

作者:kid_2412

来源:blog.csdn.net/kid_2412/article/details/76633525

背景

话说这个背景挺惨的,京东某系统使用了poi-ooxml-3.5-final做excel导出功能。起初使用该版本的poi的HSSF配合多线程生成excel,没有任何问题,后来改成了XSSF生成后上线,导出3w条数据时,cpu使用率达到了100%,内存达到了100%,打死了整个服务器!

惨绝人寰的场景:

线上环境docker单机配置如下:

- 内存:8G

- cpu:2核

- jvm:

- -Xmx:4G

- -Xms:4G

- -MaxPerm:256M

- -Xss:256K

- OGC:Parallel Old

- YGC:Parallel Scavenge

由于cpu使用率打爆,内存打爆,整个服务器处于拒绝服务状态,而呈现到前端则是应用系统大部分卡死。于是业务方不断反复点击导出按钮,状况不断扩大到集群内其他机器上,导致集群出现雪崩现象。监控系统频繁报警,同时惨遭业务方屠杀。。。

当然我们起初只是升级了版本,同时以为是多线程导致的,改为了单线程生成。当时也没有分析出问题具体出现在哪里,上线后没有出现cpu和内存打爆现象。但是,问题总要找到根源的,于是我们对这次事故做了回溯。

分析过程

由于服务器已经被打死,内存那么高,根本无法dump线上堆内存,甚至连jstack查看线程栈都无法使用。不过在自主运维平台中导出了gc信息,发现eden空间和old空间都被打满,同时yong gc和full gc都非常频繁,也就是说频繁gc没有回收掉任何对象。

下图为我本机测试的 jstat -gcutil 7068 1000 10,由于在自主化运维平台导出的结果文件被我删除了,所以只能用本机的测试,不过结果现象是相同的。

可见eden空间的s0和s1已经无法交换了,eden空间已经完全打满,old空间也一样打满,yong gc和full gc都非常频繁,cpu自然使用率高了,不过不足以打满整个cpu!现在目前定位到了fullgc没有回收垃圾,那么需要找到内存打满和为啥没回收的原因。要想找到内存打满的原因肯定需要分析heap空间对象。

那么既然线上已经无法导出heap信息了,是不是可以尝试在本地做这件事?那么俩个问题需要明确:

如何做?

由于问题出现在导出报表,并且已知升级了版本并且改成了单线程导出就解决了,同时之前使用HSSF的时候并没有出现问题,也证明了业务代码没有问题,问题出现在XSSF的版本和多线程上。所以本地可以模拟poi-ooxml-3.5-FINAL的XSSF进行大量数据的导出实验,同时需要进行多线程导出。

由于不是业务代码和业务数据产生的问题,在本地mock数据可以使用简单的大量对象构成的结构进行导出,线上30个列导出,本地测试5个列,线上是本地的6倍,线上的每一行的数据量必然要比本地的数据量大很多。同时怀疑是poi-ooxml-3.5-FINAL内存泄露或内存管理出现的问题,那么其实不需要4g内存,在2g的内存下压榨到死看看heap中大量的对象是不是poi相关的就可以了。然后再升级下版本,继续压榨一下看看会不会压死即可。

如何分析?

其实分析很简单,以往使用线上jmap dump后用mat查看内存泄露,现在由于在本地测试了,可以直接用jprofiler attach上去直接观察就可以了。

就是这个家伙,当然它是需要破解的:

idea也是有插件的:

好了,挑出线上的导出代码,写个单元测试

package cn.geapi.service;

import cn.geapi.User;

import org.apache.commons.lang3.StringUtils;

import org.apache.poi.xssf.usermodel.XSSFCell;

import org.apache.poi.xssf.usermodel.XSSFRow;

import org.apache.poi.xssf.usermodel.XSSFSheet;

import org.apache.poi.xssf.usermodel.XSSFWorkbook;

import org.junit.Test;

import java.io.ByteArrayInputStream;

import java.io.ByteArrayOutputStream;

import java.io.IOException;

import java.io.InputStream;

import java.math.BigDecimal;

import java.util.ArrayList;

import java.util.Date;

import java.util.List;

/**

* Created by kid on 2017/1/9.

*/

public class UserServiceTest {

@Test

public void testLogin() {

int size = 500000;

Listusers = new ArrayList<>(size);

User user;

for (int i = 0; i < size; i++) {

user = new User();

user.setId(Integer.toUnsignedLong(i));

user.setAge(i + 10);

user.setName("user" + i);

user.setRemark(System.currentTimeMillis() + "");

user.setSex("男");

users.add(user);

}

new Thread(() -> {

String[] columnName = {"用户id", "姓名", "年龄", "性别", "备注"};

Object[][] data = new Object[size][5];

int index = 0;

for (User u : users) {

data[index][0] = u.getId();

data[index][1] = u.getName();

data[index][2] = u.getAge();

data[index][3] = u.getSex();

data[index][4] = u.getRemark();

index++;

}

XSSFWorkbook xssfWorkbook = generateExcel("test", "test", columnName, data);

}

).start();

try {

Thread.currentThread().join();//等待子线程结束

} catch (InterruptedException e) {

e.printStackTrace();

}

}

private static XSSFWorkbook generateExcel(String sheetName, String title, String[] columnName, Object[][] data) {

XSSFWorkbook workBook = new XSSFWorkbook();

// 在workbook中添加一个sheet,对应Excel文件中的sheet

// 如果没有给定sheet名,则默认使用Sheet1

XSSFSheet sheet;

if (StringUtils.isNotBlank(sheetName)) {

sheet = workBook.createSheet(sheetName);

} else {

sheet = workBook.createSheet();

}

// 构建大标题,可以没有

XSSFRow headRow = sheet.createRow(0);

XSSFCell cell = null;

cell = headRow.createCell(0);

cell.setCellValue(title);

//大标题行的偏移

int offset = 0;

if (StringUtils.isNotBlank(title)) {

offset = 1;

}

// 构建列标题,不能为空

headRow = sheet.createRow(offset);

for (int i = 0; i < columnName.length; i++) {

cell = headRow.createCell(i);

cell.setCellValue(columnName[i]);

}

// 构建表体数据(二维数组),不能为空

for (int i = 0; i < data.length; i++) {

headRow = sheet.createRow(++offset);

for (int j = 0; j < data[0].length; j++) {

cell = headRow.createCell(j);

if (data[i][j] instanceof BigDecimal)

cell.setCellValue(((BigDecimal) data[i][j]).doubleValue());

else if (data[i][j] instanceof Double)

cell.setCellValue((Double) data[i][j]);

else if (data[i][j] instanceof Long)

cell.setCellValue((Long) data[i][j]);

else if (data[i][j] instanceof Integer)

cell.setCellValue((Integer) data[i][j]);

else if (data[i][j] instanceof Boolean)

cell.setCellValue((Boolean) data[i][j]);

else if (data[i][j] instanceof Date)

cell.setCellValue((Date) data[i][j]);

else

cell.setCellValue((String) data[i][j]);

}

}

return workBook;

}

}

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121

奔跑吧小代码!

整体情况:

1. 内存打满

2. gc无法回收掉对象

3. cpu负载非常高

CPU信息:

1. 大量cpu占用在XSSFCell.setCellValue中

2. 生成excel generateExcel就占据了所有的cpu

而后,gc回收时间过长导致了:

堆信息:

他喵的全是poi的对象!!!

这里还需要注意的是,需要验证poi-ooxml-3.5-FINAL在多线程情况下是否会出现这个问题,验证很简单,把new Thread去掉,直接在主线程导出。这里直接说明实验结果,new Thread去了依然内存爆满!

而且观察测试代码可以发现,虽然是主线程new Thread创建了个新线程,形似多线程,但是测试数据并不存在线程共享问题,没有在主线程和子线程进行资源竞争,不存在锁互斥问题。所以排除掉了多线程产生的问题。而且在写入表格字段值的时候poi也进行了加锁操作。

看看XSSF和HSSF的区别

The supplied data appears to be in the Office 2007+ XML. You are calling the part of POI that deals with OLE2 Office Documents. You need to call a different part of POI to process this data (eg XSSF instead of HSSF)

其实区别就是XSSF支持excel 2007以后的导出,HSSF只支持以前的。excel 2007以后能导出更多的数据了。

解决方案

查看poi官网的change log http://poi.apache.org/changes.html ,既然3.5-FINAL的XSSF有问题,向上查找3.5-FINAL之后的XSSF相关字样的信息,会发现在3.6中

memory usage optimization in xssf - avoid creating parentless xml beans

在xxsf进行中做了内存优化 - 避免了创建无父类的xml bean对象

所以得出结论,升级poi-oxxml版本到3.6或者更高版本!

当然,我们的线上环境已经进行了升级。

总结

  • 首先我们知道了poi性能不高

  • 其次我们需要知道我们所依赖的每个版本的特性和bug

  • 而这次事故也提醒我们,我们的应用系统并不是高可用的!

  • 面对这样的问题,我们能否做好压力测试?在没上线之前就发现这样的问题,以及在线上做好捣乱练习和容灾演练。

(完)

BAT等大厂Java面试经验总结 想获取 Java大厂面试题学习资料扫下方二维码回复「BAT」就好了回复 【加群】获取github掘金交流群回复 【电子书】获取2020电子书教程回复 【C】获取全套C语言学习知识手册回复 【Java】获取java相关的视频教程和资料回复 【爬虫】获取SpringCloud相关多的学习资料回复 【Python】即可获得Python基础到进阶的学习教程回复 【idea破解】即可获得intellij idea相关的破解教程关注我gitHub掘金,每天发掘一篇好项目,学习技术不迷路!回复 【idea激活】即可获得idea的激活方式
回复 【Java】获取java相关的视频教程和资料
回复 【SpringCloud】获取SpringCloud相关多的学习资料
回复 【python】获取全套0基础Python知识手册
回复 【2020】获取2020java相关面试题教程
回复 【加群】即可加入终端研发部相关的技术交流群为什么HTTPS是安全的
因为BitMap,白白搭进去8台服务器...
《某厂内部SQL大全 》.PDF
字节跳动一面:i++ 是线程安全的吗?
大家好,欢迎加我微信,很高兴认识你!
在华为鸿蒙 OS 上尝鲜,我的第一个“hello world”,起飞!相信自己,没有做不到的,只有想不到的在这里获得的不仅仅是技术!如果喜欢就给个“在看”

记一次悲惨的 excel 导出事件!相关推荐

  1. 记一次悲惨的 Excel 导出事件

    背景 话说这个背景挺惨的,京东某系统使用了poi-ooxml-3.5-final做excel导出功能.起初使用该版本的poi的HSSF配合多线程生成excel,没有任何问题,后来改成了XSSF生成后上 ...

  2. 记一次java实现excel导出

    新年过完了哦,小子我又来了,大家新年过的还快乐吗?反正我是只感觉到了"快,",没有感觉到"乐". 2021年的第一天,就接到新需求了.对,就是那个谁,来来,给个 ...

  3. 记一次Excel导出导致内存耗尽的问题

    今天遇到一个问题,导出线上2个月的Excel统计数据频繁导致报错,一查php-fpm日志,则是提示: PHP Fatal error: Allowed memory size of 298844160 ...

  4. 由excel导出引起的cpu 100% 和gc 的问题

    大家好,我是烤鸭:     记一次 由excel导出 导致的cpu飙升200%,jvm 内存不足. 1.  场景复现 前端页面导出Excel,之前导出4,5W条数据都没什么问题的.     今天业务突 ...

  5. Delphi实现带有格式的Excel导出功能

    功能预览 运行预览 模板样式 存储返参 导出的Excel 2. 代码实现 //执行sql的函数 procedure TForm1.GetReportData(astrsql:string); vars ...

  6. POI报表入门,excel,使用事件模型解析百万数据excel报表

    POI报表入门,excel 1.pom依赖: <?xml version="1.0" encoding="UTF-8"?> <project ...

  7. 基于ABP和Magicodes实现Excel导出操作

      前端使用的vue-element-admin框架,后端使用ABP框架,Excel导出使用的Magicodes.IE.Excel.Abp库.Excel导入和导出操作几乎一样,不再介绍.文本主要介绍E ...

  8. java中Excel导出echart图片

    java中Excel导出echart图片 1.在生成echart的前端代码生成图片代码后Echart.setOption(captestRcapEchartOption, true);后面加上以下代码 ...

  9. npoi导出文件不保存在服务器,winform NPOI excel 导出并选择保存文件路径

    public void ExcelOp(DataGridView gdv,ArrayList selHead) { if (selHead.Count==0) { MessageBox.Show(&q ...

最新文章

  1. 最小二乘法预测c语言,用最小二乘法推导本吧会员增长方程,以预测人数增长情况...
  2. ajax使用pur请求怎么传参,数组参数传递给控制器的方式
  3. c#正则匹配取出文本内容 循环输出
  4. 生成模型和判别模型_生成模型和判别模型简介
  5. 中国这10条逆天公路,火爆外网,你都认识多少?
  6. linux查看native进程,Android 分析应用程序占用native内存
  7. doc es 中type_一文带你彻底弄懂ES中的doc_values和fielddata
  8. Java连接程序数据源
  9. 使用C#开发ActiveX控件
  10. QT中如何固定窗口的大小?
  11. uc浏览器邀请码_UC密保手机不能用?冬树教你如何一招申诉成功!
  12. 拓端tecdat|R语言CRAN软件包Meta分析
  13. SoapUI接口测试实例(webservice接口)
  14. 民营医院网络咨询解答技巧
  15. php fpm 测试,php-fpm – 配置详解(转)
  16. python中文转化gb2321_使用Python进行中文繁简转换的实现代码
  17. 面向对象程序设计及C++mooc编程(第六章)--by sCh3n
  18. python发outlook邮件_通过Python发送Outlook电子邮件?
  19. linux 清除swap 数据,linux清除swap
  20. web前端 “我是有底线的”效果

热门文章

  1. 2020年劳务员-岗位技能(劳务员)试题及答案及劳务员-岗位技能(劳务员)复审考试
  2. 管家婆之垃圾清理功能
  3. SPSS折线图【012-2期】
  4. 百度Apollo中ParkingLot与ParkingSpace的区别
  5. 2022年全国中职组网络安全国赛赛题思路(仅自己一个做题的思路)——网络安全竞赛试题(7)
  6. 文件上传(人事信息管理-劳动合同)
  7. kingbase 数据备份与恢复
  8. 机器学习深度学习加强学习_加强强化学习背后的科学
  9. cisco C9K ——产品手册
  10. 基于STM32+ESP8266的HLW8032智能电表超额报警设计