转载自  一次恐怖的 Java 内存泄漏排查实战

最近在看《深入理解Java虚拟机:JVM高级特性与最佳实践》(第二版)这本书,理论+实践结合,深入浅出,强烈推荐给大家。

这两天对JVM内容进行了一个讨论,讨论的内容主要包括如下几个方面。
1)内存溢出和内存泄露的介绍?
2)如何排查和处理内存泄露?

一、内存溢出和内存泄露

一种通俗的说法。
1、内存溢出:你申请了10个字节的空间,但是你在这个空间写入11或以上字节的数据,出现溢出。
2、内存泄漏:你用new申请了一块内存,后来很长时间都不再使用了(按理应该释放),但是因为一直被某个或某些实例所持有导致 GC 不能回收,也就是该被释放的对象没有释放。点击此处查看内存泄漏更多说明。

下面具体介绍。

1.1 内存溢出

java.lang.OutOfMemoryError,是指程序在申请内存时,没有足够的内存空间供其使用,出现OutOfMemoryError。点击此处查看内存泄漏更多说明。

产生原因
产生该错误的原因主要包括:

  • JVM内存过小。

  • 程序不严密,产生了过多的垃圾。

程序体现
一般情况下,在程序上的体现为

  • 内存中加载的数据量过于庞大,如一次从数据库取出过多数据。

  • 集合类中有对对象的引用,使用完后未清空,使得JVM不能回收。

  • 代码中存在死循环或循环产生过多重复的对象实体。

  • 使用的第三方软件中的BUG。

  • 启动参数内存值设定的过小。

错误提示
此错误常见的错误提示:

tomcat:java.lang.OutOfMemoryError: PermGen space
tomcat:java.lang.OutOfMemoryError: Java heap space
weblogic:Root cause of ServletException java.lang.OutOfMemoryError
resin:java.lang.OutOfMemoryError
java:java.lang.OutOfMemoryError

解决方法

1)增加JVM的内存大小
对于tomcat容器,找到tomcat在电脑中的安装目录,进入这个目录,然后进入bin目录中,在window环境下找到bin目录中的catalina.bat,在linux环境下找到catalina.sh。
编辑catalina.bat文件,找到JAVA_OPTS(具体来说是 set "JAVA_OPTS=%JAVA_OPTS% %LOGGING_MANAGER%")这个选项的位置,这个参数是Java启动的时候,需要的启动参数。
也可以在操作系统的环境变量中对JAVA_OPTS进行设置,因为tomcat在启动的时候,也会读取操作系统中的环境变量的值,进行加载。
如果是修改了操作系统的环境变量,需要重启机器,再重启tomcat,如果修改的是tomcat配置文件,需要将配置文件保存,然后重启tomcat,设置就能生效了。

2)优化程序,释放垃圾
主要思路就是避免程序体现上出现的情况。避免死循环,防止一次载入太多的数据,提高程序健壮型及时释放。因此,从根本上解决Java内存溢出的唯一方法就是修改程序,及时地释放没用的对象,释放内存空间。

1.2 内存泄露

Memory Leak,是指程序在申请内存后,无法释放已申请的内存空间,一次内存泄露危害可以忽略,但内存泄露堆积后果很严重,无论多少内存,迟早会被占光。

在Java中,内存泄漏就是存在一些被分配的对象,这些对象有下面两个特点。
1)首先,这些对象是可达的,即在有向图中,存在通路可以与其相连;
2)其次,这些对象是无用的,即程序以后不会再使用这些对象。

如果对象满足这两个条件,这些对象就可以判定为Java中的内存泄漏,这些对象不会被GC所回收,然而它却占用内存。

关于内存泄露的处理页就是提高程序的健壮型,因为内存泄露是纯代码层面的问题。点击此处查看内存泄漏更多说明。

1.3 内存溢出和内存泄露的联系

内存泄露会最终会导致内存溢出。

相同点:都会导致应用程序运行出现问题,性能下降或挂起。
不同点:1) 内存泄露是导致内存溢出的原因之一,内存泄露积累起来将导致内存溢出。2) 内存泄露可以通过完善代码来避免,内存溢出可以通过调整配置来减少发生频率,但无法彻底避免。

二、一个Java内存泄漏的排查案例

某个业务系统在一段时间突然变慢,我们怀疑是因为出现内存泄露问题导致的,于是踏上排查之路。

2.1 确定频繁Full GC现象

首先通过“虚拟机进程状况工具:jps”找出正在运行的虚拟机进程,最主要是找出这个进程在本地虚拟机的唯一ID(LVMID,Local Virtual Machine Identifier),因为在后面的排查过程中都是需要这个LVMID来确定要监控的是哪一个虚拟机进程。

同时,对于本地虚拟机进程来说,LVMID与操作系统的进程ID(PID,Process Identifier)是一致的,使用Windows的任务管理器或Unix的ps命令也可以查询到虚拟机进程的LVMID。

jps命令格式为:
jps [ options ] [ hostid ]
使用命令如下:
使用jps:jps -l

使用ps:ps aux | grep tomat 找到你需要监控的ID(假设为20954),再利用“虚拟机统计信息监视工具:jstat”监视虚拟机各种运行状态信息。

jstat命令格式为:
jstat [ option vmid [interval[s|ms] [count]] ]
使用命令如下:
jstat -gcutil 20954 1000
意思是每1000毫秒查询一次,一直查。gcutil的意思是已使用空间站总空间的百分比。

结果如下图:

jstat执行结果

查询结果表明:这台服务器的新生代Eden区(E,表示Eden)使用了28.30%(最后)的空间,两个Survivor区(S0、S1,表示Survivor0、Survivor1)分别是0和8.93%,老年代(O,表示Old)使用了87.33%。程序运行以来共发生Minor GC(YGC,表示Young GC)101次,总耗时1.961秒,发生Full GC(FGC,表示Full GC)7次,Full GC总耗时3.022秒,总的耗时(GCT,表示GC Time)为4.983秒。

2.2 找出导致频繁Full GC的原因

分析方法通常有两种:
1)把堆dump下来再用MAT等工具进行分析,但dump堆要花较长的时间,并且文件巨大,再从服务器上拖回本地导入工具,这个过程有些折腾,不到万不得已最好别这么干。

2)更轻量级的在线分析,使用“Java内存影像工具:jmap”生成堆转储快照(一般称为headdump或dump文件)。
jmap命令格式:
jmap [ option ] vmid
使用命令如下:
jmap -histo:live 20954
查看存活的对象情况,如下图所示:

存活对象

按照一位IT友的说法,数据不正常,十有八九就是泄露的。在我这个图上对象还是挺正常的。

我在网上找了一位博友的不正常数据,如下:

可以看出HashTable中的元素有5000多万,占用内存大约1.5G的样子。这肯定不正常。

2.3 定位到代码

定位带代码,有很多种方法,比如前面提到的通过MAT查看Histogram即可找出是哪块代码。——我以前是使用这个方法。也可以使用BTrace,我没有使用过。

一次恐怖的 Java 内存泄漏排查实战相关推荐

  1. 小心踩雷,一次Java内存泄漏排查实战

    " 前些日子小组内安排值班,轮流看顾我们的服务,主要做一些报警邮件处理.Bug 排查.运营 issue 处理的事.工作日还好,无论干什么都要上班的,若是轮到周末,那这一天算是毁了. 不知道是 ...

  2. Java 内存泄漏排查,新技能+1

    点击关注公众号,Java干货及时送达 来源 | https://zhenbianshu.github.io/ 前些日子小组内安排值班,轮流看顾我们的服务,主要做一些报警邮件处理.Bug 排查.运营 i ...

  3. 又一次 Java 内存泄漏排查,新技能+1

    点击关注公众号,Java干货及时送达 前些日子小组内安排值班,轮流看顾我们的服务,主要做一些报警邮件处理.Bug 排查.运营 issue 处理的事.工作日还好,无论干什么都要上班的,若是轮到周末,那这 ...

  4. 一次 Java 内存泄漏排查过程,学习学习

    人人都会犯错,但一些错误是如此的荒谬,我想不通怎么会有人犯这种错误.更没想到的是,这种事竟发生在了我们身上.当然,这种东西只有事后才能发现真相.接下来,我将讲述一系列最近在我们一个应用上犯过的这种错误 ...

  5. 一次毕生难忘的 Java 内存泄漏排查经历

    作者:yizhe 原文:http://www.importnew.com/29591.html 人人都会犯错,但一些错误是如此的荒谬,我想不通怎么会有人犯这种错误.更没想到的是,这种事竟发生在了我们身 ...

  6. 一次 Java 内存泄漏排查过程,涨姿势

    人人都会犯错,但一些错误是如此的荒谬,我想不通怎么会有人犯这种错误.更没想到的是,这种事竟发生在了我们身上.当然,这种东西只有事后才能发现真相.接下来,我将讲述一系列最近在我们一个应用上犯过的这种错误 ...

  7. Java内存泄漏的排查

    1.内存溢出 一种通俗的说法. 1.内存溢出:你申请了10个字节的空间,但是你在这个空间写入11或以上字节的数据,出现溢出. 2.内存泄漏:你用new申请了一块内存,后来很长时间都不再使用了(按理应该 ...

  8. unity如何检测内存泄漏_如何排查Java内存泄漏?看懂这一篇就够用了

    原文:https://www.toptal.com/java/hunting-memory-leaks-in-java 作者:Jose Ferreirade Souza Filho 译者:Emma来源 ...

  9. 什么是内存泄露?该怎么排查?Java内存泄漏策略

    什么是内存泄漏 内存泄漏:对象已经没有被应用程序使用,但是垃圾回收器没办法移除它们,因为还在被引用着. 在Java中,内存泄漏就是存在一些被分配的对象,这些对象有下面两个特点,首先,这些对象是可达的, ...

最新文章

  1. 推荐一些算法方面的好书
  2. MySQL数据库Raid存储方案
  3. Pixhawk飞控源码目录结构及编译流程分析
  4. 前端学习(1659):前端系列实战课程之瀑布流实现思路
  5. 第十三题:子类要调用继承自父类的方法,必须使用super关键字。
  6. 利用触摸屏获取事件坐标
  7. 彩虹屁支持java吗_IDEA版本彩虹屁插件idea-rainbow-fart,一个在你编程时疯狂称赞你的 IDEA扩展插件...
  8. 【codeforces】【比赛题解】#937 CF Round #467 (Div. 2)
  9. DPDK Release 20.02
  10. C语言文件操作(文件读写)
  11. ls命令显示颜色含义详解
  12. 大数据和Hadoop培训能否满足市场需求
  13. java绘图- 绘图用法(基于Graphics2D)
  14. 每天一道大厂SQL题【Day01】
  15. Cardboard -VR全景浏览
  16. 网络测试的window bat脚本分析
  17. i3-10110U和i5 1035g7 哪个好
  18. 在centos7 下安装python3.6
  19. js  计算是今天多少周
  20. PTA 7-194 循环结构 —— 中国古代著名算题。趣味题目:物不知其数。

热门文章

  1. leetcode242. 有效的字母异位词(两种方法map或数组)
  2. 考研961数据结构c语言版真题,严蔚敏数据结构C语言版考研真题库
  3. 堆栈的定义与操作-顺序存储,链式存储(C语言)
  4. [Java基础]多态基础
  5. C++map容器-大小和互换
  6. matlab 判断鼠标按下,Matlab:如何通过使用回调来获取当前鼠标在点击位置
  7. Redis分布式锁奥义
  8. 数据结构----归并排序
  9. P3203 [HNOI2010]弹飞绵羊
  10. P4551 最长异或路径