Eden、from、to 的默认比例是 8:1:1,所以只会造成 10% 的空间浪费。
-XX:SurvivorRatio 进行配置的(默认为 8)。
关于对象老不老,是通过它的年龄(age)来判断的。每当发生一次 Minor GC,存活下来的对象年龄都会加 1。直到达到一定的阈值,就会把这些“老顽固”给提升到老年代。
这个阈值,可以通过参数 ‐XX:+MaxTenuringThreshold 进行配置,最大值是 15,因为它是用 4bit 存储的。
通过 -XX:+PrintCommandLineFlags 参数,可以查看当前 Java 版本默认使用的垃圾回收器。
以下是一些配置参数:
-XX:+UseSerialGC 年轻代和老年代都用串行收集器
-XX:+UseParNewGC 年轻代使用 ParNew,老年代使用 Serial Old
-XX:+UseParallelGC 年轻代使用 ParallerGC,老年代使用 Serial Old
-XX:+UseParallelOldGC 新生代和老年代都使用并行收集器
-XX:+UseConcMarkSweepGC,表示年轻代使用 ParNew,老年代的用 CMS
-XX:+UseG1GC 使用 G1垃圾回收器
-XX:+UseZGC 使用 ZGC 垃圾回收器
尤其注意 -XX:+UseParNewGC 这个参数,已经在 Java9 中就被抛弃了。很多程序(比如 ES)会报这个错误,不要感到奇怪。
线上使用最多的垃圾回收器,就有 CMS 和 G1,以及 Java8 默认的 Parallel Scavenge。
CMS 的设置参数:-XX:+UseConcMarkSweepGC。
Java8 的默认参数:-XX:+UseParallelGC。
Java13 的默认参数:-XX:+UseG1GC。
CMS垃圾回收器
CMS 提供了参数 CMSScavengeBeforeRemark,可以在进入重新标记之前强制进行一次 Minor GC。
由于 CMS 在执行过程中,用户线程还需要运行,那就需要保证有充足的内存空间供用户使用。如果等到老年代空间快满了,再开启这个回收过程,用户线程可能会产生“Concurrent Mode Failure”的错误,这时会临时启用 Serial Old 收集器来重新进行老年代的垃圾收集,这样停顿时间就很长了(STW)。
这部分空间预留,一般在 30% 左右即可,那么能用的大概只有 70%。参数 -XX:CMSInitiatingOccupancyFraction 用来配置这个比例(记得要首先开启参数UseCMSInitiatingOccupancyOnly)。也就是说,当老年代的使用率达到 70%,就会触发 GC 了。如果你的系统老年代增长不是太快,可以调高这个参数,降低内存回收的次数。
其实,这个比率非常不好设置。一般在堆大小小于 2GB 的时候,都不会考虑 CMS 垃圾回收器。
另外,CMS 对老年代回收的时候,并没有内存的整理阶段。这就造成程序在长时间运行之后,碎片太多。如果你申请一个稍大的对象,就会引起分配失败。
CMS 提供了两个参数来解决这个问题:
(1) UseCMSCompactAtFullCollection(默认开启),表示在要进行 Full GC 的时候,进行内存碎片整理。内存整理的过程是无法并发的,所以停顿时间会变长。
(2)CMSFullGCsBeforeCompaction,每隔多少次不压缩的 Full GC 后,执行一次带压缩的 Full GC。默认值为 0,表示每次进入 Full GC 时都进行碎片整理。
所以,预留空间加上内存的碎片,使用 CMS 垃圾回收器的老年代,留给我们的空间就不是太多,这也是 CMS 的一个弱点。
CMS 的 trade-off。
优势:
低延迟,尤其对于大堆来说。大部分垃圾回收过程并发执行。
劣势:
内存碎片问题。Full GC 的整理阶段,会造成较长时间的停顿。
需要预留空间,用来分配收集阶段产生的“浮动垃圾”。
使用更多的 CPU 资源,在应用运行的同时进行堆扫描。
G1 的思路说起来也类似,它不要求每次都把垃圾清理的干干净净,它只是努力做它认为对的事情。
G1垃圾回收器
我们要求 G1,在任意 1 秒的时间内,停顿不得超过 10ms,这就是在给它制定 KPI。G1 会尽量达成这个目标,它能够推算出本次要收集的大体区域,以增量的方式完成收集。
这也是使用 G1 垃圾回收器不得不设置的一个参数:
-XX:MaxGCPauseMillis=10
Region 的大小,可以通过参数进行设置:
-XX:G1HeapRegionSize=M
当整个堆内存使用达到一定比例(默认是 45%),并发标记阶段就会被启动。这个比例也是可以调整的,通过参数 -XX:InitiatingHeapOccupancyPercent 进行配置。
通过 Concurrent Marking 阶段,我们已经统计了老年代的垃圾占比。在 Minor GC 之后,如果判断这个占比达到了某个阈值,下次就会触发 Mixed GC。这个阈值,由 -XX:G1HeapWastePercent 参数进行设置(默认是堆大小的 5%)。因为这种情况下, GC 会花费很多的时间但是回收到的内存却很少。所以这个参数也是可以调整 Mixed GC 的频率的。
还有参数 G1MixedGCCountTarget,用于控制一次并发标记之后,最多执行 Mixed GC 的次数。