您的位置:首页 > 其它

记录一次OOM堆内存溢出

2018-01-25 19:24 169 查看

事件概述

页面点击导出所有数据, 由于数据量过大,消费方通过分页查询的方式从服务方分页获取所有数据,存放在list中。
然后将list中大概六万条数据转化为Excel文件下载, 此时发生java.lang.OutOfMemoryError: Java heap space,堆内存溢出。


/**
* 分页查询获取所有的数据
*
* @param pageReqForTop
* @param employeePartyId
* @param totalRow 总记录数
* @return
* @throws Exception
* @author ex-huangyajun 2018年01月20日 15:11
*/
private List<EquipDateMaintainVo> getEquipDateMaintainVoListPage(PageReqForTop<EquipDateMaintainVo> pageReqForTop,
Long employeePartyId, Integer totalRow) throws Exception{
List<EquipDateMaintainVo> exportList = new ArrayList<>();
//每页查询的数量
final int pageSize = 5000;
//计算页数
int totalPage = (totalRow - 1) / pageSize + 1;

logger.info("---查询总的记录数 totalRow = {},计算页数 totalPage = {}, 每页显示行数 pageSize = {}", totalRow, totalPage, pageSize);
for (int curPage = 0; curPage < totalPage; curPage++) {
//当前页起始行
pageReqForTop.setCurPage(curPage + 1);
pageReqForTop.setPageSize(pageSize);
Response<PageResForTop<List<EquipDateMaintainVo>>> result = equipDateMaintainServiceClient
.pageEquipDateMaintain(pageReqForTop, employeePartyId);
if (result == null || result.getResult() == null) {
return null;
}
if (result.getCode() != FacadeEnums.OK.code) {
throw new BizException(result.getMsg());
}
List<EquipDateMaintainVo> list = result.getResult().getData();
exportList.addAll(list);
logger.info("---分页导出,第【{}】次查询成功,查询出List size = 【{}】", curPage + 1, list.size());
}
logger.info("---已经将所有数据封装到exportList ,exportList.size = {}", exportList.size());
return exportList;
}


如何查出问题所在:

打开linux控制台:

$ jps //是JDK 1.5提供的一个显示当前所有java进程pid的命令
28461 Jps
22153 Bootstrap


$ jstat -gcutil 22153 1000
S0     S1     E      O      P     YGC     YGCT    FGC    FGCT     GCT
0.00   5.39  13.39  25.25  99.78     57    1.048     9    1.733    2.781
0.00   5.39  13.39  25.25  99.78     57    1.048     9    1.733    2.781

//S0  — 年轻代中第一个survivor(幸存区)已使用的占当前容量百分比
//S1  — 年轻代中第二个survivor(幸存区)已使用的占当前容量百分比
//E   — 年轻代中Eden(伊甸园)已使用的占当前容量百分比
//O   — Heap上的 Old space 区已使用空间的百分比
//P   — Perm space 区已使用空间的百分比
//YGC — 从应用程序启动到采样时发生 Young GC 的次数
//YGCT– 从应用程序启动到采样时 Young GC 所用的时间(单位秒)
//FGC — 从应用程序启动到采样时发生 Full GC 的次数
//FGCT– 从应用程序启动到采样时 Full GC 所用的时间(单位秒)
//GCT — 从应用程序启动到采样时用于垃圾回收的总时间(单位秒)


$ jstat -gc 22153 1000
S0C    S1C    S0U    S1U      EC       EU        OC         OU       PC     PU    YGC     YGCT    FGC    FGCT     GCT
17472.0 17472.0  0.0   941.9  139776.0 18776.8   349568.0   88272.3   70528.0 70375.1     57    1.048   9      1.733    2.781
17472.0 17472.0  0.0   941.9  139776.0 18776.8   349568.0   88272.3   70528.0 70375.1     57    1.048   9      1.733    2.781


查看java 堆(heap)使用情况,执行命令:

$ jmap -heap 22153


Attaching to process ID 22153, please wait...
Debugger attached successfully.
Server compiler detected.
JVM version is 24.79-b02

using thread-local object allocation.
Mark Sweep Compact GC //GC 方式

Heap Configuration: //堆内存初始化配置
MinHeapFreeRatio = 0 //对应jvm启动参数-XX:MinHeapFreeRatio设置JVM堆最小空闲比率(default 40)
MaxHeapFreeRatio = 100 //对应jvm启动参数 -XX:MaxHeapFreeRatio设置JVM堆最大空闲比率(default 70)
MaxHeapSize      = 2082471936 (1986.0MB) //对应jvm启动参数-XX:MaxHeapSize=设置JVM堆的最大大小
NewSize          = 1310720 (1.25MB)//对应jvm启动参数-XX:NewSize=设置JVM堆的‘新生代’的默认大小
MaxNewSize       = 17592186044415 MB//对应jvm启动参数-XX:MaxNewSize=设置JVM堆的‘新生代’的最大大小
OldSize          = 5439488 (5.1875MB)//对应jvm启动参数-XX:OldSize=<value>:设置JVM堆的‘老生代’的大小
NewRatio         = 2 //对应jvm启动参数-XX:NewRatio=:‘新生代’和‘老生代’的大小比率
SurvivorRatio    = 8 //对应jvm启动参数-XX:SurvivorRatio=设置年轻代中Eden区与Survivor区的大小比值
PermSize         = 21757952 (20.75MB)  //对应jvm启动参数-XX:PermSize=<value>:设置JVM堆的‘永生代’的初始大小
MaxPermSize      = 85983232 (82.0MB)//对应jvm启动参数-XX:MaxPermSize=<value>:设置JVM堆的‘永生代’的最大大小
G1HeapRegionSize = 0 (0.0MB)

Heap Usage://堆内存使用情况
PS Young Generation
Eden Space://Eden区内存分布
capacity = 33030144 (31.5MB)//Eden区总容量
used     = 1524040 (1.4534378051757812MB)  //Eden区已使用
free     = 31506104 (30.04656219482422MB)  //Eden区剩余容量
4.614088270399305% used //Eden区使用比率
From Space:  //其中一个Survivor区的内存分布
capacity = 5242880 (5.0MB)
used     = 0 (0.0MB)
free     = 5242880 (5.0MB)
0.0% used
To Space:  //另一个Survivor区的内存分布
capacity = 5242880 (5.0MB)
used     = 0 (0.0MB)
free     = 5242880 (5.0MB)
0.0% used
PS Old Generation //当前的Old区内存分布
capacity = 86507520 (82.5MB)
used     = 0 (0.0MB)
free     = 86507520 (82.5MB)
0.0% used
PS Perm Generation//当前的 “永生代” 内存分布
capacity = 22020096 (21.0MB)
used     = 2496528 (2.3808746337890625MB)
free     = 19523568 (18.619125366210938MB)
11.337498256138392% used

670 interned Strings occupying 43720 bytes.


histo[:live] 打印每个class的实例数目,内存占用,类全名信息. VM的内部类名字开头会加上前缀”*”. 如果live子参数加上后,只统计活的对象数量.

$ jmap -histo:live 22153

num     #instances         #bytes  class name

----------------------------------------------

1:         65220        9755240  <constMethodKlass>

2:         65220        8880384  <methodKlass>

3:         11721        8252112  [B

4:          6300        6784040  <constantPoolKlass>

5:         75224        6218208  [C

6:         93969        5163280  <symbolKlass>


最后,修改配置文件增大堆内存:

$ cd /opt/apache-tomcat-8.0.23/bin
打开catalina.sh文件,添加JAVA_OPTS:

CATALINA_OPTS="$CATALINA_OPTS $UI_DEBUG"
JAVA_OPTS="-Xms512m -Xmx512m -server"
# OS specific support.  $var _must_ be set to either true or false.
cygwin=false
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: