JVM的问题排查及优化方向
报错排查
java.lang.StackOverflowError
- 栈帧过多:递归调用,栈帧不断扩展,无法申请到更多的内存容纳栈帧,导致栈内存溢出;
- 栈帧过大;
- -Xss过小;
java.lang.OutOfMemoryError: Java heap space
- 内存过小,无法容纳程序正常运行所不断创建的对象大小;
- 超大对象占用过多内存,又无法回收;
- 内存泄漏,导致无法回收;
java.lang.OutOfMemoryError: GC overhead limit
- 这种情况通常是由于内存泄漏,GC的回收效率很低,GC占用时间长,但是回收内存很低,就会触发此OOM;
java.lang.OutOfMemoryError: MetaSpace
- 程序可能动态创建太多的class;
java.lang.OutOfMemoryError: Direct buffer memory
- Java 允许应用程序通过 Direct ByteBuffer 直接访问堆外内存,当访问的堆外内存大于默认64MB时,触发OOM;
- 此OOM通常是IO线程引发的,如NIO;
- 可以通过:-XX:MaxDirectMemorySize调整堆外内存大小;
内存泄漏
内存泄漏严重,往往会导致OOM; 如果频繁Full GC,并且效果不佳,可能是内存泄漏; 常见原因: 1、连接资源未正常关闭; 2、ThreadLocal未Remove; 3、数据重复;可能是缓存时,缓存的对象未实现
equals
、hashcode
导致重复存储;
优化核心
堆区是主要的调优区域:
1、提升对象从年轻代进入老年代的门槛;
2、降低 FullGC 次数;(核心)
常用JVM参数
参数 | 作用 |
---|---|
-Xms600m | 堆的起始内存 |
-Xmx600m | 堆的最大内存 |
-Xmn200m | 新生代大小 |
-XX:SurvivorRation=8 | Eden占新生代的8/10,剩余2/10,From/To 平分 |
-XX:NewRation=2 | 老年代/新生代比例大小 |
-XX:+UseAdaptiveSizePolicy | 动态调整 JVM 各区大小以及 gc 年龄阈值 |
-XX:MaxTenuringThreshold=15 | 晋升阈值:默认15,超出此阈值,对象移入老年代 |
-XX:TargetSurvivorRatio=90 | Survivor 区占用达到 90%,再将对象移入老年代 |
-XX:PretenureSizeThreshold=3145728 | 如果对象大小超过这个值,将直接分配在老年代 |
-XX:+PrintGCDetails | GC详情 |
-XX:+ScavengeBeforeFullGC | FullGC前执行一次MinorGC |
- Heap 大小:
- -Xms600m:堆的起始内存
- -Xmx600m:堆的最大内存
- (超出触发 MajorGC,无法回收则抛:OutOfMemoryError)
- 自适应策略(JDK1.7)
- -XX:+UseAdaptiveSizePolicy:动态调整 JVM 各区大小以及 gc 年龄阈值;
- 默认初始:老/新 = 2,Eden/Survivor = 8:2,默认年龄阈值 15;
- 每次 GC 后,动态计算大小 Eden/Survivor 的内存分配;
- 默认开启,且推荐不要关闭,除非一定要使用特定的 JVM 分区大小;
以下 2 个参数开启前提:关闭自适应策略
-
新生代老年代比例:
- -XX:NewRation=2:老年代/新生代比例大小
-
新生代内分区比例:
- -XX:SurvivorRation=8:Eden占新生代的比例,剩余 From/To 平分
- -XX:InitialSurvivorRatio=8 -XX:+UseAdaptiveSizePolocy: 初始化比例为8,并动态调整新生代比例
- 一些垃圾收集器,直接默认动态调整;
-
控制对象进入老年代的参数:
- -XX:MaxTenuringThreshold=15:默认 15,超出此阈值,对象移入老年代;
- -XX:TargetSurvivorRatio=90:Survivor 区占用达到 90%,再将对象移入老年代
- 通常是占用达到 50%,按照年龄排序后,超出 50%的大年龄对象,会在 MinorGC 时移入老年代;
- -XX:PretenureSizeThreshold=3145728:如果对象大小超过这个值,将直接分配在老年代
如何确定参数
- 首先要确定线上机器的配置:CPU核数、内存大小;
- 根据CPU核数,配置合适的线程池;根据-Xss计算出线程可能占用的内存大小;
- 比如300线程,默认栈内存1Mb,则需要300Mb
- 根据机器内存大小,配置堆区大小,一般配置机器内存的一半;
- -Xms=4096、-Xmx=4096一般设置一样大;
- 元空间一般512Mb,够用了;
- -XX:MetaspaceSize=512M -XX:MaxMetaspaceSize=512M
- 根据机器配置、业务场景,选择合适的垃圾收集器;
- 内存小于8G,使用CMS:-XX:+UseConcMarkSweepGC -XX:+UseParNewGC
- 大于8G可以考虑G1:-XX:+UseG1GC
- 内存小于8G,使用CMS: