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. Android-TextView 控件常用属性以及基本用法

    github地址:https://github.com/1165863642/TextViewDemo 前言 这是我第一次写博客,第一次的笔记,不足之处多谅解.开门见山,这一篇博客主要讲一下在Andr ...

  2. 钉钉扫码登录web网站

    钉钉扫码登录网站 前言  由于本公司前后台分离,这里主要讲述后台的实现逻辑与过程,前端相关的一略而过.前端我们采用的是把二维码内嵌到我们的网页中. 流程如下: 1.登录钉钉后台创建一个企业应用 2.根 ...

  3. 【mongoDB高级篇③】综合实战(1): 分析国家地震数据

    数据准备 下载国家地震数据 http://data.earthquake.cn/data/ 通过navicat导入到数据库,方便和mysql语句做对比 shard分片集群配置 # step 1 mkd ...

  4. java中获取系统的当前时间

    转自:http://www.cnblogs.com/Matrix54/archive/2012/05/01/2478158.html 一. 获取当前系统时间和日期并格式化输出: import java ...

  5. linux源

    系统:centos7 x86_64 一.配置本地yum源 1.1加载光驱 1.2挂载到系统 注:如果要长期使用最好把整个镜像文件拷贝到系统下 1.3配置文件 路径/etc/yum.repos.d/ 打 ...

  6. c/c++ 线性表之单向链表

    c/c++ 线性表之单向链表 线性表之单向链表 不是存放在连续的内存空间,链表中的每个节点的next都指向下一个节点,最后一个节点的下一个节点是NULL. 真实的第一个节点是头节点,头节点不存放数据, ...

  7. 轻量的web框架Bottle

    简洁的web框架Bottle 简介 Bottle是一个非常简洁,轻量web框架,与django形成鲜明的对比,它只由一个单文件组成,文件总共只有3700多行代码,依赖只有python标准库.但是麻雀虽 ...

  8. python根据关键字以及id值爬取图虫网上高质量大图

    import requests import re import os import time print("ready....") def downPic(dirs, keywo ...

  9. vue 之 .sync 修饰符

    在一些情况下,我们可能会需要对一个 prop (父子组件传递数据的属性) 进行“双向绑定”. 在vue 1.x 中的 .sync 修饰符所提供的功能.当一个子组件改变了一个带 .sync 的prop的 ...

  10. June 5. 2018 Week 23rd Tuesday

    Learn to let go and be clear of where you really want to head for. 学会放手,同时也要弄清楚自己的真正所爱. From Kissing ...