简略来演示一下 OOM 的剖析和实战。
间接上代码:
public class Demo4 {public static void main(String[] args) {List<Dandan> list = new ArrayList<>();
while (true){list.add(new Dandan());
}
}
}
class Dandan{
}
JVM 参数:
-XX:+UseParNewGC -XX:+UseConcMarkSweepGC -Xms10m -Xmx10m -XX:+PrintGCDetails -Xloggc:gc_dandan.log -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=./
运行后的日志:
java.lang.OutOfMemoryError: Java heap space
Dumping heap to ./java_pid22788.hprof ...
Heap dump file created [13244840 bytes in 0.050 secs]
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
at java.util.Arrays.copyOf(Arrays.java:3210)
at java.util.Arrays.copyOf(Arrays.java:3181)
at java.util.ArrayList.grow(ArrayList.java:267)
at java.util.ArrayList.ensureExplicitCapacity(ArrayList.java:241)
at java.util.ArrayList.ensureCapacityInternal(ArrayList.java:233)
at java.util.ArrayList.add(ArrayList.java:464)
at com.hailintang.demo.jdk8.gc.oom.Demo4.main(Demo4.java:11)
Process finished with exit code 1
java.lang.OutOfMemoryError: Java heap space
首先,这里明确通知咱们内存泄露的中央是:堆
因为配置了参数:-XX:+HeapDumpOnOutOfMemoryError
会在程序产生 OOM 的时候,主动生成一个文件:java_pid22788.hprof
命名格局为:java_pid(程序的过程号).hprof
接下来当然是用 MAT 来剖析:hprof 文件
在剖析之前,咱们重点关注什么?
- 第一,哪些对象占用大量内存。
- 第二,对象被谁援用。(就是要晓得为什么它无奈开释的意思)
- 第三,定位到具体哪行代码,进行剖析问题
关上 hprof 文件, 如果想要剖析内存泄露的话,就勾选红色框框。
这里,咱们首先看看那些对象占用大量内存。按红色框框 Histogram 按钮
第一,哪些对象占用大量内存
进入 histogram 页面
进来页面后,你一眼就能发现哪个对象占用的内存最多。
比方这里就是显著的 class com.hailintang.demo.jdk8.gc.oom.Dandan 这个类占用了过多内存。
这里说了,这个 Dandan 类的对象有 360146 个。到目前为止,咱们临时确定了 Dandan 对象占用了大量内存。
第二,对象被谁援用
接下来,咱们看看内存过多的对象是被谁援用。
那这个时候须要用到 MAT 的 dominator-tree: 就是用来剖析对象之间的关系的工具。
而后你能够看到哪些线程创立了过多的对象。
比方这里创立过多对象的线程就是 main thread
而后你开展这个 main thread 看看到底创立了什么对象
使得 main thread 占用这么多内存
点开一看,好家伙
发现是一个 java.lang.Object[] 数组。
持续开展
发现数组外面全是 Dandan 对象
说到这里,其实就水落石出了
通过层层开展。main thread 线程创立的对象和在 histogram 界面中占用过多内存的对象 Dandan 对上了。
第三,定位到具体哪行代码,进行剖析问题
找到援用后,最初一步就是要定位一下具体是哪行代码创立了这么多对象?
这个时候须要另一个视图 thread_overview。红圈所示
thread_overview 的性能:展现出 JVM 的所有线程以及每个线程以及线程过后的办法调用栈,还有每个办法创立了哪些对象。
特地阐明一下: 栈,后进先出,所以,看图的时候,从下往上看
进入 thread_overview 界面
找到 main thread 线程,点开看看
你能够疾速看到 Demo4.java:11
这个时候,你还不是特地确定的。须要点开持续看看
能够看到这里确实创立了大量的 Dandan 对象。
到这里你再联合一下大头菜的代码:
好了,到这里,基本上曾经找到了具体是哪行代码引起的 OOM 问题。
简略总结一下
总结
咱们的方法论是:
- 第一,哪些对象占用大量内存。—— 对应的 MAT 的 histogram
- 第二,对象被谁援用。(就是要晓得为什么它无奈开释的意思)—— 对应 MAT 的 dominator-tree
- 第三,定位到具体哪行代码,进行剖析问题 —- 对应 MAT 的 Thread-overview
而后,咱们依据方法论,联合 MAT 工具,一个一个步骤来进行排查。目标是为了找到具体哪行代码引起的 OOM 问题。
下面这个是一个比较简单的 OOM 问题。只是一个 Demo, 目标是为了通知你如何应用 MAT 来剖析 OOM 问题。
如果简单一点的我的项目,在第三步中,如果出问题的代码是其余第三方中间件,比方 tomcat,jetty,rpc 等框架引起的 OOM,这个时候,还须要你对这些中间件比拟相熟能力定位问题。如果出问题的代码是业务代码,这个时候,是须要邀请负责该项目标开发工程师来进行定位代码的。
其实,大家齐全能够把 MAT 这个定位 OOM 问题的方法论,用到理论工作。
好了,明天的技术分享就到这里。
有问题,欢送留言。