JVM垃圾收集器细节补充

禁用System.gc()

System.gc()会显式直接触发Full GC,同时对老年代和新生代进行回收。JVM的内存回收都是自动的,一般情况下不需要我们手动触发GC。因此如果要在程序中禁用,可以通过-XX:+DisableExplicitGC禁用。当程序中使用了System.gc(),就相当于调用了一个空函数。

对象何时进入老年代

一般情况下,新创建的对象会出现在新生代的Eden区。对于存活时间长的对象,进入老年代的情况有以下2种:

普通对象晋升老年代

JVM中提供了-XX:MaxTenuringThreshold=15这个参数来指定,普通对象晋升老年代经历的GC的最大次数。这个参数值默认是15,当然对象往往不到15次GC就已经进入了老年代。

晋升中还有一个比较重要的参数,-XX:TargetSurvivorRatio=50。它用来指定suvivor区的使用率达到50%时(默认值),触发晋升老年代。

大对象直接进入老年代

通常来说,我们对于Java项目的堆分配,老年代会占到一大半的容量。所以当新生代(Eden/Survivor)容量不足时,新创建的大对象会直接进入老年代。

我们可以通过-XX:PretenureSizeThreshold(单位字节)来指定直接晋升老年代的对象大小。但是在测试时往往会出现以下情况,对象达到设置大小并未进入老年代。这是因为JVM在使用内存空间时,会优先使用一块TLAB的区域。

TLAB(Thread Local Allocation Buffer)是线程本地分配缓存。它占用了Eden区的空间,JVM会为每一个Java线程分配一块TLAB空间。启用参数是:-XX:+UseTLAB(JVM默认开启)。

对象分配简要流程

我们所熟知的是对象是在对上分配的,但是对象的分配大致会经历以下几个流程。首先会尝试在栈上分配,栈上分配的对象会随着方法块的执行结束,而自动被回收。如果失败了,则会尝试TLAB分配。如果再失败了,则会检查是否满足直接进入老年代的条件,满足则在老年代分配。如果不满足,则在Eden区分配。简要流程如下所示:

finalize()对GC的影响

finalize()方法类似于C++中的析构函数,它在对象即将被GC回收器回收的时候执行1次。但是尽量不要使用finalize()对资源进行释放,尽量不要重写该方法,原因如下:

finalize()可能会造成对象复活

执行时间是没有保障的,它完全由GC线程决定。极端情况下,如果不发生GC,finalize()将不会执行

如写的不好,则有可能严重影响GC性能

finalize()是由FinalizerThread线程处理的。每个即将被回收的并且包含有finalize()方法的对象,都会被在正式回收前加入FinalizerThread的执行队列(为ReferenceQueue引用队列)。

FinalizerThread的工作过程和FinalizerThread执行队列关系如下所示:

​​​​​​​

常用GC参数整理

串行收集器相关

-XX:+UseSerialGC:在新生代和老年代使用串行收集器。

-XX:SurvivorRatio:设置Eden区和Survivor区的比例。设置为8,则两个Survivor区与一个Eden区的比值为2:8,一个Survivor区占整个年轻代的1/10。

-XX:PretenureSizeThreshold:设置大对象进入老年代的阀值(单位字节)。

-XX:MaxTenuringThreshold:设置进入老年代年龄的最大值,每一次新生代GC都会加1(默认15)。

并行GC相关

-XX:+UseParNewGC:新生代使用并行收集器。

-XX:+UseParallelOldGC:老年代使用并行回收。

-XX:ParallelGCThreads:设置用户GC的线程数。通常情况下可以和CPU个数相同,但CPU较多的情况下,可以相对设置小一些。

-XX:MaxGCPauseMillis:设置GC最大停顿时间。

-XX:GCTimeRatio:设置吞吐量大小。取值0-100之间的整数。如设置为n,则系统花费不超过1/(n+1)的时间用于GC。

-XX:+UseAdaptiveSizePolicy:打开自适应GC策略。这种模式下,JVM的各个参数都将会被动态调整,以达到堆大小、吞吐量和停顿时间的平衡。

CMS相关

-XX:+UseConcMarkSweepGC:新生代使用并行回收器,老年代使用CMS+串行回收。

-XX:ParallelCMSThreads:设定CMS的线程数量。

-XX:CMSInitiatingOccupancyFraction:设置CMS收集器在老年代使用多少后触发GC,默认68%。

-XX:+UseCMSCompactAtFullCollection:设置CMS收集器在完成垃圾收集后,是否要进行一次碎片整理。

-XX:CMSFullGCsBeforeCompaction:设定进行多少次CMS回收后,进行一次内存压缩。

-XX:+CMSClassUnloadingEnabled:允许对元数据区进行回收。

-XX:CMSInitiatingPermOccupancyFraction:当永久区使用率达到这一百分比时,启动CMS回收。(前提是CMSClassUnloadingEnabled开启)

-XX:UseCMSInitiatingOccupancyOnly:表示只在达到阀值的时候才进行CMS回收。

-XX:+CMSIncrementalMode:使用增量模式,比较适合单CPU。JDK8中废弃,JDK9中已移除。

G1相关

-XX:+UseG1GC:使用G1收集器。

-XX:MaxGCPauseMillis:设置最大停顿时间。

-XX:GCPauseIntervalMillis:设置停顿间隔时间。

TLAB相关

-XX:+UseTLAB:开启TLAB分配。

-XX:+PrintTLAB:打印TLAB相关分配信息。

-XX:TLABSize:设置TLAB大小。

-XX:+ResizeTLAB:自动调整TLAB大小。

其他

-XX:+DisableExplicitGC:禁用显示GC。

-XX:+ExplicitGCInvokesConcurrent:使用并发的方式处理显示GC。


参考:《实战Java虚拟机》