1、G1 垃圾回收器
G1 能更少的 "Stop the World" ,能同时对新生代老年代进行垃圾回收。
G1 将 Java 堆内存拆分为多个大小相等的 Region,并且新生代和老年代只是逻辑上的概念。
最大的特点:设置一个垃圾回收的预期停顿时间。可以让 G1 垃圾回收器保证达到开发人员手动设置的 "Stop the World" 的时间。
G1 对垃圾回收导致的系统停顿可控:追踪每个 Region 里的回收价值。就是清楚每个 Region 里的对象多少是垃圾,如果对该 Region 进行垃圾回收,需要耗费多少时间,回收多少垃圾对象。
停顿时间参数: -XX:MaxGCPauseMills,默认200ms。
核心思想:设定了垃圾回收的预期停顿时间后,G1 尽量控制在设置的时间范围内,最少回收时间,回收更多的垃圾。
JVM 最多有2048个 Region,Region 的大小必须是2的倍数。Region 新生代和老年代占比其实是动态变化的,新生代初始默认5%,最大默认60%。
触发垃圾回收,还是 Eden 区占满了对象。除了停顿时间是预设的,其垃圾回收过程是和新生代垃圾回收过程 PerNew一样。
对象从新生代进入老年代,也是和 CMS 垃圾回收器的机制相同,除了大对象。因为会专门分配 Region 来存大对象。(一个对象的大小超过一个 Region 内存的50%)
混合回收:前三步操作和 CMS 回收相似。只有最后一步是混合回收,多次停顿回收,复制 region,并且清除对象。Full GC 失败也和 CMS 回收相相似,变为单线程回收,并Stop the World。
在老年代占内存的45%开始混合回收 MixedGC,并且也会回收大对象。
小总结:一些负载很低的系统,JVM 本身是没什么必要调优的。没有复杂度和高负载的系统,JVM 一般很少会出现问题。
适合 G1 回收器的场景:追求低延迟的场景,或者大内存机器。(内存太大,如果常规垃圾回收器,就会一次性回收很多垃圾,停顿时间过长)
2、JVM 调优实战
学会看 GC 日志,与分析 GC 日志。
当 Survivor 区域存放不下 Eden 区存活的对象时,Eden 区存活的对象,一部分会移动到老年代,一部分会移动到 Survivor 区。
通过 jstat -gc PID de 命令,可以分析出一些数据:
新生代增长的速率(观察 eden 区的被占用内存的变化);
YGC 触发的频率(多久触发一次 YGC);
YGC 的耗时;
每次 YGC 后有多少对象存活下来(发生YGC 后看 survivor 区新增对象的大小);
每次 YGC 过后有多少对象进入老年代(发生 YGC 后看老年代新增对象的大小);
老年代增长的速率;
FGC 触发频率;
FGC 的耗时。
3、极高QPS怎么优化 JVM
思路:
用 jstat 分析一下 JVM 运行情况,判断每次 YGC 后存活对象有多少,增加 Survivor 区的内存,避免对象快速进入老年代。
但是老年代还是会发生 FullGC,这时就需要整理老年代的内存碎片。
分析:
1)、极高QPS,则会迅速产生对象,迅速占满 Eden 区,则进行 YGC。而进行 YGC 的时候,同时又有很多请求产生对象,则会有很多对象在 YGC 的时候存活。
2)、如果存活的对象很多,则可能导致 Surivivor 区装不下存活对象,就会导致对象大量进入老年代。则会频繁出发 OGC。
3)、在 OGC 的时候,因为回收算法是标记-清理算法,则会在老年代中产生大量内存碎片。
解决:
1)、参数:-XX:+UserCMSCompactAtFullCollection -XX:CMSFullGCsBeforeCompaction=5 可以把5修改为0。含义是在发生几次 FullGC 后,对老年代的内存碎片进行整理,空出大量内存空间。
因为内存碎片还会导致 FullGC 的频率,因为内存碎片会越来越多,能放下的对象越来越少。
2)、参数:-XX:+CMSparallellnitialMarkEnabled,这个参数会在 CMS 垃圾回收器的 初始标记 阶段开启多线程并发执行。主要是减少 Stop the world 的时间。
3)、参数:-XX:+CMSScavengeBeforeRemark,这个参数会在 CMS 的重新标记阶段前进行一次 YGC。回收一些年轻代里没有人引用的对象,减少 CMS 在重新标记阶段扫描的对象,减少耗时。
4、JVM 的一些参数整理
-XX:CMSInitiatingOccupancyFraction=92 和 -XX:+UseCMSInitiatingOccupancyOnly 配套使用。如果仅设置前者,CMS 垃圾回收器第一次会采用内存占老年代 92% 但是后续会根据运行时采集的数据来进行 GC 周期。设置了后者,就会每次固定在内存占老年代 92% 时进行垃圾回收。
-XX:CMSParallellnitialMarkEnabled 表示在初始标记阶段,多线程执行以减少 STW;
-XX:CMSScavengeBeforeRemark 表示在重新标记阶段之前,执行 MinorGC 减少重新标记的时间;
-XX:CMSParallelRemarkEnable 表示在重新标记阶段,多线程执行以减少 STW;
5、小总结
频繁出发 FullGC de 三种可能:
1)、内存分配不合理,导致对象频繁进入老年代;(Survivor 过小,或者新生代过小,并且未开启空间担保规则,开启了动态年龄判断)
2)、存在内存泄露等问题,就是内存里驻留了大量的对象塞满了老年代,导致稍微有一些对象进入老年代就触发 FullGC;
3)、永久代里的类太多,触发了 FullGC。