一、jvm调优
调优目的:
减少full GC的频率
缩短full GC停顿时间
jvm调优主要就是调整下面两个指标:
停顿时间:垃圾收集器做垃圾回收中断应用执行时间。 -xx:MacGCPauseMillis
吞吐量:垃圾收集的时间和总时间的占比:1/(1+n),吞吐量为 1-1/(1+n)
GC调优步骤:
-
打印GC日志
-
分析日志得到关键性指标
-
分析GC原因,调优jvm参数
二、记一次oom异常
1、收到告警:90%-old-gen-used-on-127.0.0.1:8080 应用老年代内存使用率超过90%
**2、**监控平台上并没有集成内存分析的工具,需要自行dump分析
拿到dump文件
首先,登录生产环境,
然后, jps 找到当前java应用的进程pid,或在 Linux 服务器上用 top -c 命令来找是哪个进程 。
最后,可通过 jmap 命令来获取 HeapDump 文件 : jmap -dump:format=b,file=filename pid 例如: jmap -dump:format=b,file=dumpfilename 3331 最后的数字是进程 id 。
分析dump文件
工具:JDK自带的 VisualVM 或者 外部内存分析工具MAT
使用软件分析可得出:
1、会提示具体是执行到哪个线程抛出的 oom 异常。而且可以点进去查看更详细的信息。
2、点击 Classes ,然后按照 size 排序。 会发现某一个类占用比特别高。右键这个类 Show in Instances View ,会跳转到 Instances 视图。
3、然后找 GC Root,根据 GC 规则,如果能找到 GC Root ,那么这个类是不会被回收的。
最终找到原因是因为 异常数据:遇到大对象data 是一个列表,也就是在执行查询后返回几十万数据。而且此类查询一直在执行。占用的内存很难释放。
相似参考:
实际案例:
频繁FGC的真凶原来是它 juejin.cn/post/684490…
2. 使用 VisualVM 分析堆转储文件 (Heap Dump)
当OOM发生,生成了 .hprof 文件后,你就可以用 jvisualvm 来分析了。
-
打开 VisualVM。
-
载入堆转储文件:通过菜单
File->Load...,选择生成的.hprof文件。 -
分析“罪证”:文件加载后,VisualVM 会提供多个视图,其中最有用的是 “Classes” 和 “OQL Console” 视图。
重点查看区域:
-
Summary (摘要):先看这里,确认是因为堆内存溢出(Java heap space) 还是元空间溢出(Metaspace)。这决定了你接下来的分析方向。
-
如果是 Java heap space:继续下面的步骤。
-
如果是 Metaspace:问题通常是加载了过多的类(如动态代理、反射生成类过多),你需要查看类加载器的信息。
-
-
Classes (类视图):
-
这里按类名分组,列出了所有类的实例数量和总大小。
-
点击表头“Size (Bytes)”进行降序排序。排在最前面的几个类,就是占用内存最多的“嫌疑犯”。
-
-
Instances (实例视图):
-
在“Classes”视图中双击你怀疑的类(比如
byte[]或String或某个自定义类),会切换到该类的实例视图。 -
你可以看到这个类的成千上万个具体实例。
-
右键点击某个实例 -> “Show In Instances View” -> “by incoming references”。这会显示是谁持有着对这个实例的引用,从而帮你一步步追溯到你的业务代码中创建并持有这些对象的根源。这是找到问题代码的关键步骤。
-
3. 实战案例:一个典型的OOM分析流程
假设你的程序因为 Java heap space 而OOM了。
-
你载入堆转储文件后,在 Classes 视图按 Size 排序。
-
发现排名第一的是
byte[],占用了 90% 的内存。 -
你双击
byte[],在 Instances 视图里看到一大堆巨大的字节数组。 -
你右键其中一个巨大的
byte[]-> “Show In Instances View” -> “by incoming references”。 -
VisualVM 会显示这个
byte[]被哪个对象引用着。比如,你发现它被一个BufferedImage对象引用着。 -
你再右键这个
BufferedImage对象,同样查看“incoming references”,可能发现它被你的某个User对象引用着,而该User对象又被一个静态的Map所引用。 -
至此, root cause 就找到了:你的代码里有一个静态的
Map缓存了用户信息,其中包括用户的大头像图片,而这个缓存只增不减,最终吃光了所有堆内存。