由excel导出引起的cpu 100% 和gc 的问题
大家好,我是烤鸭:
记一次 由excel导出 导致的cpu飙升200%,jvm 内存不足。
1. 场景复现
前端页面导出Excel,之前导出4,5W条数据都没什么问题的。
今天业务突然反馈说导出不了了,我试着导出了2w数据,发现页面卡住了,
没有响应了,查日志,报错如下。
java.lang.IllegalStateException: Cannot call sendRedirect() after the response has been committed
at org.apache.catalina.connector.ResponseFacade.sendRedirect(ResponseFacade.java:488)
at javax.servlet.http.HttpServletResponseWrapper.sendRedirect(HttpServletResponseWrapper.java:138)
查看了导出方法,发现如果repsonse在响应过程中抛出异常的话,就会有类似的问题。
正常的话,因为需要把错误信息带到页面,catch之后再return到对应的页面。
但如果是上图所示的write方法,如果这地方报异常,就会出现
Cannot call sendRedirect() after the response has been committed 的异常。
输出文件的代码如下,如果这时候异常。
/**
* 输出到客户端
* @param fileName 输出文件名
*/
public ExportExcel write(HttpServletResponse response, String fileName) throws IOException{
response.reset();
response.setContentType("application/octet-stream; charset=utf-8");
response.setHeader("Content-Disposition", "attachment;fileName=" + new String(fileName.getBytes("GBK"), "iso-8859-1")); //中文文件名处理
write(response.getOutputStream());
return this;
}
知道这是写法的问题,但是并没有什么好的办法解决。
因为 异常需要通过addMessage(redirectAttributes, e.getMessage()) 带到重定向的页面。
但是转念一想,这个问题不应该在这地方出现,因为之前用了很长时间是没有这个问题的。
2. 寻找问题
再次执行导出的时候,监测了一下cpu,为啥,第六感吧。
top #查看cpu进程运行
ps -ef|grep tomcat_xxx #查看tomcat 的 pid
pid 是 19021
发现 19021 的进程爆表了...如下图
jstat -gcutil 19021 5000 #每隔5秒打印一次gc
又去看了gc的情况,每隔5秒打印一次gc,各个空间全都满了。而且YGC和FGC的频次和时间在迅速增加。(下图中的时间还没到满的时候,后来确实都100了)
还好,服务器还撑得住,应该是数据量不是特别大,没有报java.lang.OutOfMemoryError。
jmap -heap 19021 #查看堆内存详细信息
jstack 19021 #查看线程问题(是否死锁)
其实我的问题在这就解决了,想了想最近改动过的代码,基本就锁定了问题,导出时候的问题,
后来查了excel工具类的代码提交,发现在创建cell单元格的时候,没有判断样式是否存在,
只要是空单元格,就新创建一个单元格样式。
改之前:
CellStyle style = wb.createCellStyle();
cell.setCellStyle(style);
改之后:
if (val != null){
CellStyle style = styles.get("data_column_"+column);
if (style == null){
style = wb.createCellStyle();
style.cloneStyleFrom(styles.get("data"+(align>=1&&align<=3?align:"")));
style.setDataFormat(wb.createDataFormat().getFormat(cellFormatString));
styles.put("data_column_" + column, style);
}
cell.setCellStyle(style);
}
不要小瞧了这个判空,原来的写法会多创建2w行*20列=40w个对象,导致各个空间的满了原因。
如果还没找到问题的话,就输出gc日志,慢慢查。
jmap -histo 9021>xxx.log #输出gc日志到文件
3. 总结
如果是gc或者内存问题。
获取当前进程pid
ps -ef|grep tomcat_xxx 或者 ps -ef|grep java 或者 jps
查看cpu利用率
top
监测gc
jstat -gcutil pid 5000
查看堆内存详细信息
jmap -heap pid
查看是否死锁情况
jstack pid
还找不到的话,输出gc的日志慢慢找
jmap -histo pid>xxx.logs
- 分页控件和几个相关控件的源代码
- Python编程中的反模式
- Python机器学习库:Scikit-Learn简介
- 很简单的企业管理器---我写程序的方式,几个自定义控件。
- 使命必达: 深入剖析WCF的可靠会话[原理揭秘篇](上)
- 其实添加数据也可以这样简单——表单的第三步抽象(针对UI及后置代码)
- 为Symfony2和Redis正名,基于PHP的10亿请求/周网站打造
- 如何使用Python基线预测进行时间序列预测
- 如何使用统计显着性检验来解释机器学习结果
- 其实添加数据也可以这样简单——表单的第一步抽象(针对数据访问层)《怪怪设计论: 抽象无处不在 》有感
- WCF服务端运行时架构体系详解[上篇]
- 使命必达: 深入剖析WCF的可靠会话[编程篇](下)
- 在网页里让文本框只能输入数字的一种方法。外加回车换Tab
- 如何用Python从零开始实现简单的线性回归
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- Django Swagger接口文档生成
- ggplot2|从0开始绘制箱线图
- ggplot2|从0开始绘制直方图
- ggplot2|从0开始绘制PCA图
- Python字典
- 如何用R语言绘制生成正态分布图表
- ggplot2-plotly|让你的火山图“活”过来
- 小数据| 描述性统计(Python/R 实现)
- ggplot2|发散性“正负”图
- R语言蒙特卡洛计算和快速傅立叶变换计算矩生成函数
- pheatmap|暴雨暂歇,“热图”来袭!!!
- 统一服务消息接口报48001错误
- ggplot2|ggpubr进行“paper”组图合并
- PostgreSQL drop table 空间不释放的问题解决
- R语言预测人口死亡率:用李·卡特模型、非线性模型进行平滑估计