模拟实战中排查堆内存溢出( java.lang.OutOfMemoryError: Java heap space)的问题。堆内存溢出的缘由:通常都是建立了大量的对象,这些对象一直被引用着,没法被GC垃圾回收掉,最终致使堆内存被占满,没有足够的空间存放新建立的对象时,就会出现堆内存溢出问题。java
在实际的业务场景中出现内存溢出的问题,排查起来通常是十分困难繁琐的,本文将经过结合一个简单的实例来阐述排查的具体思路和步骤。程序员
注意:在实际场景中,通常都是部署在Linux服务器中的项目报出内存溢出的问题;为了尽量还原出实际场景,本文也是将提早编写好的能够触发内存溢出的代码并打包成可运行的Jar包,而后放到服务器中执行的。
// 建立一个Java类 public class OutOfMemory { private String test; public OutOfMemory(String test){ this.test = test; } } // 模拟内存溢出的发生 public class TestOOM { public static void main(String[] args) { List<OutOfMemory> list = new ArrayList<OutOfMemory>(); while(true){ /** * 无限建立OutOfMemory对象,直至将堆空间占满,而且建立的OutOfMemory对象一直被list集合对象引用着, * 致使GC也没法回收,最终出现堆内存溢出问题 */ list.add(new OutOfMemory("5656")); System.out.println("5656"); } } }
代码编写完成后,使用开发工具导出 可运行的Jar包(TestOOM.jar)
能够直接使用centos或者Red Hat等均可以;
①、可以使用xshell、xftp工具将可运行的Jar包(Jar包叫:TestOOM.jar)放入到服务器中;
②、使用命令执行Jar包;命令:
java -Xms40m -Xmx70m -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/usr/tmp -jar TestOOM.jarshell
注意:为了尽快模拟发生堆内存溢出,因此在启动Jar包时,设置了一些参数;参数解析:
1)、 -Xms40m 初始堆大小设置为40m
2)、 -Xmx70m 最大堆大小设置为70m
3)、 -XX:+HeapDumpOnOutOfMemoryError 出现堆内存溢出时,自动导出堆内存 dump 快照
4)、 -XX:HeapDumpPath=/usr/tmp 设置导出的堆内存快照的存放地址为 /usr/tmp小程序
①、jps命令:此命令是用来查询与Java相关的进程的,并输出进程号;下图就是展现上面运行的Jar包的进程号:windows
②、jmap命令: jmap -heap 3324 此命令是查询出进程号为 3324 的JVM中堆内存信息;以下图:centos
在图中能够发现堆内存中新生代、年老代中 free 可用空间愈来愈小,这预示着即将会发生GC垃圾回收,从而使堆腾出更多的空间存放新建立的对象。
③、jstat命令:使用其监控JVM的性能信息;例如:在本次排查内存溢出的问题中,会使用 jstat 命令监控 JVM的 GC垃圾回收的状况;
命令: jstat -gcutil 3324 1000 意思是每1000毫秒查询一次进程号为3324 的JVM的GC垃圾回收的状况;以下图:服务器
(1)、 YGC(堆中新生代GC)、FGC( FULL GC)为何触发频率这么快呢?
答:因为堆内存空间不够用了,须要经过GC垃圾回收将一些空间进行回收,用于存放新建立的对象。
( 2)、当堆内存空间不够用时,GC具体会发生什么呢?
答:
1)、当堆中的新生代空间不够用时,会触发YGC,对堆中新生代空间进行垃圾回收,同时垃圾回收后剩余存活的对象会移动到堆中
老年代存储,因此每次YGC后,堆中年老代中存储的对象数量会增大;2)、当堆的新生代即将发生YGC时,若是发现新生代中存活下来的对象比堆中年老代中剩余的可用空间大的话,就会直接不进行YGC,
而会直接触发FGC,FULL GC会对整个堆空间(新生代、老年代)以及方法区/永久代进行垃圾回收;工具
扩展:堆的结构图:性能
①、使用XFTP等工具将服务器中的快照文件导出,本文堆内存快照文件是以 hprof 为后缀的文件;导出快照文件后,能够经过JDK自带的 jvisualvm.exe 分析工具打开进行分析。学习
jvisualvm.exe 是在哪里呢?(以 windows 系统为例)
它是在 JDK的安装目录中的bin目录下的。
如图:
②、使用 jvisualvm.exe 导入快照文件,如图:
(1)、
(2)、
(3)、
经过分析堆内存快照获得的结论:
经过第(3)张图,能够发现堆内存中有一个实例对象的占比为 99.9%,能够肯定是因为这个实例对象大量建立致使堆内存的溢出;
说到这,能够回过头去看下咱们本身编写的能够触发堆内存溢出的小程序,发现正是因为在 while(true) 死循环 中无线建立 OutOfMemory对象,致使堆内存空间被耗尽。
结语:
经过上面的实战小例子,咱们能够大致了解到在出现堆内存溢出时的排查步骤,可是在实际的场景中,这种状况可能会更加的复杂多变;好比说,上面的那个小例子在出现的堆内存溢出时自动生成的堆内存快照文件大小就达到了100多m,若是在实际的场景中,这个多是很是巨大的,这时可能就会发生快照分析工具没法导入堆内存快照。因此说,咱们须要在平时经过不断的学习,才能在将来出现问题时,能尽快定位问题并解决问题;程序员不光是能编写好代码,还须要有解决问题的能力。
一切看文章不点赞都是“耍流氓”,嘿嘿ヾ(◍°∇°◍)ノ゙!开个玩笑,动一动你的小手,点赞就完事了,你每一个人出一份力量(点赞 + 评论)就会让更多的学习者加入进来!很是感谢! ̄ω ̄=