1为什么需要System.gc()
1、除了堆内存以外,其他的内存,有些也是需要 GC 的。例如:MetaSpace,CodeCache,Direct Buffer,MMap Buffer 等等。早期在 Java 8 之前的 JVM,对于这些内存回收的机制并不完善,很多情况下都需要 FullGC 扫描整个堆才能确定这些区域中哪些内存可以回收。
2、对于 WeakReference,无论是 Young GC 还是 FullGC 就会被回收;SoftReference 只有在 FullGC 的时候才会被回收。当我们程序想主动对于这些引用进行回收的时候,需要能触发 GC 的方法,这就用到了System.gc()。
2System.gc() 背后的原理
System.gc()实际上调用的是RunTime.getRunTime().gc():
public static void gc() {
Runtime.getRuntime().gc();
//这个方法是一个 native 方法:public native void gc();
}
JVM源码:
JVM_ENTRY_NO_ENV(void, JVM_GC(void))
JVMWrapper("JVM_GC");
//如果没有将JVM启动参数 DisableExplicitGC 设置为 false,则执行 GC,GC 原因是 System.gc 触发,对应 GCCause::_java_lang_system_gc
if (!DisableExplicitGC) {
Universe::heap()->collect(GCCause::_java_lang_system_gc);
}
JVM_END
首先,根据 DisableExplicitGC
JVM 启动参数的状态:确定是否会 GC,如果需要 GC,不同 GC 会有不同的处理。
G1 GC 的处理
如果是 System.gc() 触发的 GC,G1 GC 会根据 ExplicitGCInvokesConcurrent 这个 JVM 参数决定是默认 GC (轻量 GC,YoungGC)还是 FullGC。
ZGC 的处理
直接不处理,不支持通过 System.gc() 触发 GC。
Shenandoah(沈南多啊) GC 的处理
Shenandoah 的处理和 G1 GC 的类似:先判断是不是用户明确触发的 GC,然后通过 DisableExplicitGC
JVM 参数判断是否可以 GC:如果可以,则请求 GC,阻塞等待 GC 请求被处理。然后根据 ExplicitGCInvokesConcurrent 这个 JVM 参数决定是默认 GC (轻量并行 GC,YoungGC)还是 FullGC。
其中,DisableExplicitGC && ExplicitGCInvokesConcurrent
DisableExplicitGC:是否禁用显式 GC,默认是不禁用的。
ExplicitGCInvokesConcurrent:对于显式 GC,是执行轻量并行 GC (YoungGC)还是 FullGC,如果为 true 则是执行轻量并行 GC (YoungGC),false 则是执行 FullGC。
3STW(Stop The World)
可达性分析算法中枚举根节点(GC ROOTS)会导致所有java执行线程停顿,分析工作必须在一个能确保一致性的快照中进行。所有的GC都会有STW。G1也不能完全的避免STW,尽可能的缩短暂停时间。
4垃圾回收的并行与并发
并行:多条垃圾回收线程并行工作,但此时用户线程仍处于等待状态【ParNew 、Parallel Scavenge 、Parallel Old】
串行:单线程执行。
并发(Concurrent):用户线程与垃圾回收线程同时执行(但不一定是并行的,可能会发生交替执行),垃圾回收线程在执行时不会停顿用户程序的运行;用户程序继续运行,而垃圾回收程序运行在另一个CPU上【CMS、G1】
5安全点与安全区域
程序运行时,并非在所有的地方都能停顿下来开始GC,只有在特定的位置才能停顿下来GC,这个位置称之为“安全点”。如何在GC发生时,检查所有线程都跑到最近的安全点停顿下来? 主动式中断:设置一个中断标志,各个线程运行到Safe point的时候主动轮询这个标志,如果中断为真,则将自己进行中断挂起。
但是安全点机制保证了程序执行时,在不长的时间内就会遇到可进入GC的safePoint 。但是在程序 sleep blocked状态,这时候线程就无法响应JVM中断请求,我们就需要使用安全区域去解决这个问题。安全区域指:在一段代码中,对象的引用关系不会发生变化,在这个区域中的任何位置开始GC都是安全的。实际执行时:当线程运行到Safe Region的代码时,首先标识已经进入了safe Region,如果这段时间内发生了GC,JVM会忽略标识为safe Region 状态的线程,当线程即将离开safe Region时 会检查JVM时候已经完成了GC 如果完成了就继续运行,否则,则需要等待直到可以安全离开safe region的信号为止。