1、情况概述

公司以前的某报名系统,项目启动后,在经过用户一段时间的使用之后,项目响应便开始变得极其缓慢,最后几乎毫无反应。日志里输出了一些似乎无关痛痒的异常,逐步修复,项目仍然出现这种情况,且 “项目启动 -> 服务无响应” 这段时间并不稳定。直到在被反复折磨的这几天里终于日志抓到了几个异常,都是 javax.servlet.ServletException: java.lang.OutOfMemoryError: Java heap space

2、异常分析

JVM在启动时默认设置可调配的内存空间为物理内存的1/64但小于1G,如果该空间的可用空间不足 2%,则抛出异常 OutOfMemoryError : Java heap space

项目中的日志模块并没能输出可追溯的内存溢出,只能先排除一些猜想:
  • 项目中几乎不涉及大图片加载,流不关闭等情况可以排除
  • 项目中的对象模型并不复杂,JVM的初始参数足够使用,所以单纯调整JVM的参数设置不是个好办法

在了解一下定位排错的方式后,发现这么个东西:JVM Heap Dump(堆转储文件),Heap Dump 记录了JVM中堆内存运行的情况,可以使用JDK提供的命令 jmap 生成,命令格式如下:
jmap -dump:live,format=b,file=heap-dump.bin <pid>
1
 
1
jmap -dump:live,format=b,file=heap-dump.bin <pid>

其中 pid 是JVM进程的id,heap-dump.bin 是生成的文件名称,会在执行命令的目录下生成该文件

注:在执行命令生成 dump 文件的过程中,曾报错 "Insufficient memory or insufficient privileges to attach",这是因为权限问题,调用系统服务启动的tomcat和命令行执行命令看上去都在同个administrator用户下,其实不然。解决方法是将 tomcat 以 startup.bat 启动,再在命令行调用 jmap 即可。

分析 dump 文件的工具也有不少,这里使用了很多人都推荐的 Eclipse Memory Analyzer(MAT),这是 Eclipse 提供的一款用于 Heap Dump 文件的工具,有插件的形式,也可以独立运行。

使用该工具打开生成的 dump 文件,缓慢分析载入后弹出选框,选择 Leak Suspects Report:

 Dominator Tree :支配树,列出Heap Dump中处于活跃状态中的最大的几个对象,默认按 retained size进行排序,因此很容易找到占用内存最多的对象。

使用工具的支配树功能,看到如下:

两个最高占比,而奇怪的在于之中:

总占比 29.85%,但是之中最大的对象竟然也就只占了 2.41%,怎么回事?仔细一看,除开前几个对象之外,后面全部都是 Examinee 对象,数量之多,下面的黑体提示 "Total: 25 of 228,841 entries; 228,816",剩余二十多万个对象,展开一看全是 Examinee 和相关 Hibernate 的 EntityEntry 对象!那么造成内存溢出的问题就显而易见了,内存中加载的数据量过于庞大,可能是循环引用造成的内存泄漏,也可能是对象产出过快垃圾回收无法及时处理。

3、错误定位

跟用户进行沟通后,了解到其主要是在进行考生报名添加的操作,于是进行了模拟,发现在添加报名时发送了一个请求响应很慢,数据量也很大:

响应耗时2s,数据量竟然有2MB,点开一看,正是大量的考生信息数据,足足有30000+条:

看了下这个请求数据的作用,是在拿给前端的一个插件进行自动选择用的:

再看后台的代码也很粗暴,数据没做处理,抓出来直接全丢给前台了,这个过程中当然也就生成了成千上万个 Examinee 对象:

因为这个方法是在每次添加考生报名的时候都会触发,而用户在进行考生报名时添加操作很频繁,如果多个用户同时进行添加操作,那么短时间内产生的考生对象 Examinee 将直线上升,垃圾回收清理不及时,于是内存溢出的异常也就随之而来了。

知道了因果,那么把前端的数据抓取方式改一下就解决了。

修改之前,系统异常时的 dump 文件足有 280MB,修改之后系统稳定运行,生成的 dump 文件大概只有 100MB 左右了,完结撒花。

4、回顾和经验

这个项目前年就投入使用,去年也仅是增加了微信支付等和核心业务没有太大关联的相关功能,直到今年才暴露这个问题,其原因也正是因为数据量随着时间在不断增加,以往数据量小,哪怕数据完全加载也没有压力,但是现在完全加载的话,内存就吃不消了。

这也是为什么我之前使用 "项目重启" 的方式来恢复使用,但每次的效果却越来越差的原因,因为随着使用数据量也越来越大了。

这次排错的两点收获:
  • 学会了最基本的内存分析方式,通过 dump 文件和 MAT 工具
  • 明白了某些功能在生产运行的过程中,可能会随着数据量和业务情况的不断庞大而性能下降,在编写代码初期就要尽量预估将来数据量的发展趋势,以做出稳定合理的算法

5、参考链接


记一次 OutOfMemoryError: Java heap space 的排错的更多相关文章

  1. java head space/ java.lang.OutOfMemoryError: Java heap space内存溢出

    上一篇JMX/JConsole调试本地还可以在centos6.5 服务器上进行监控有个问题端口只开放22那么设置的9998端口 你怎么都连不上怎么监控?(如果大神知道还望指点,个人见解) 线上项目出现 ...

  2. Tomcat报java.lang.OutOfMemoryError: Java heap space错误停止运行如何解决

    最近开发的一个商业项目,部署完成后,经常出现Tomcat挂掉的现象,报的异常是:java.lang.OutOfMemoryError: Java heap space,上网google了一下,了解了一 ...

  3. MyCAT报java.lang.OutOfMemoryError: Java heap space

    早上同事反映,mycat又假死了,估计还是内存溢出,查看了一下错误日志. INFO | jvm | // :: | java.lang.OutOfMemoryError: Java heap spac ...

  4. Tomcat 启动项目报错 java.lang.OutOfMemoryError: Java heap space

    近日使用myeclipse 部署web项目,启动tomcat时报错: SEVERE: Error waiting for multi-thread deployment of directories ...

  5. 应用jacob组件造成的内存溢出解决方案(java.lang.OutOfMemoryError: Java heap space)

    http://www.educity.cn/wenda/351088.html 使用jacob组件造成的内存溢出解决方案(java.lang.OutOfMemoryError: Java heap s ...

  6. java.lang.OutOfMemoryError: Java heap space

    java.lang.OutOfMemoryError: Java heap space 原因:内存溢出,内存一直申请一直占用,无法回收 解决方法:定时重启下服务,

  7. java.lang.OutOfMemoryError: Java heap space解决方法

    引起java.lang.OutOfMemoryError: Java heap space异常,可能是由JAVA的堆栈设置太小的原因 根据网上的答案大致有以下两种解决方法: 1.在D:/apache- ...

  8. 【转】java.lang.OutOfMemoryError: Java heap space的解决

    原文地址:http://blog.sina.com.cn/s/blog_4b12778b0100v0bb.html Myeclipse下java.lang.OutOfMemoryError: Java ...

  9. Hadoop执行作业时报错:java.lang.OutOfMemoryError: Java heap space

    常常被一些用户问到,说“为什么我的mapreduce作业总是运行到某个阶段就报出如下错误,然后失败呢?以前同一个作业没出现过的呀?” 10/01/10 12:48:01 INFO mapred.Job ...

随机推荐

  1. iOS------自动查找项目中不用的图片资源

    注意:删除的时候要谨慎!别什么图都删了,看看对项目有没有作用.这个插件有时也会有一定的误差. 具体操作步骤: 1.去github上下载LSUnusedResources(下载地址:https://gi ...

  2. Android图片的Base64编码与解码

    Base64是网络上最常见的用于传输8Bit字节码的编码方式之一,Base64就是一种基于64个可打印字符来表示二进制数据的方法. Base64编码是从二进制到字符的过程,可用于在HTTP环境下传递较 ...

  3. Flask路由与蓝图Blueprint

    需求分析: 当一个庞大的系统中有很多小模块,在分配路由的时候怎么处理呢?全部都堆到一个py程序中,调用@app.route? 显然这是很不明智的,因为当有几十个模块需要写路由的时候,这样程序员写着写着 ...

  4. 如何设置访问内网web项目

    1.若我的项目搭建在一个linux虚拟机上 2.在内网的一台电脑做以下配置 3.测试访问 ******************************************************* ...

  5. 关于PID的如何修改的FAQ

    1.如何查询支付宝账号对应的PID: 2.意外绑错或者想修改PID流程: (1)和业务联系拿到商家平台支付宝变更协议书填写(模版如下,可在附件中下载) 致:杭州银盒宝成科技有限公司 本人因      ...

  6. [20190319]shared pool latch与library cache latch的简单探究.txt

    [20190319]shared pool latch与library cache latch的简单探究.txt --//昨天看Oracle DBA手记3:数据库性能优化与内部原理解析.pdf 电子书 ...

  7. OkHttp简单使用

    OkHttp是一个轻量优秀的网络框架.用于替代HttpUrlConnection和Apache HttpClient(android API23 6.0里已移除HttpClient,) 使用方法: 1 ...

  8. c 指针函数 vs 函数指针

    指针函数,函数指针 #include <stdio.h> int max(int a, int b){ return a > b ? a : b; } //函数指针,2个int参数, ...

  9. Windows Server 2016-系统安装软硬件要求

    本章为大家补充介绍安装 Windows Server 2016的最低系统要求. 如果安装时选择通过"服务器核心"选项进行安装,则应注意,没有安装任何 GUI 组件,并且将不能使用服 ...

  10. 集合抽象数据类型的C语言实现

    链表是实现集合的一种理想的方式.将List以typedef的方式重命名为Set.这样做能保留链表简洁的特性,还能使集合具有了一些多态的特性. 使用这种方法的最大好处就是可以使用list_next来遍历 ...