【垃圾回收】常见面试题解析
GC是JVM中极高频会被问到的问题,一方面是因为GC这块儿的确具有较多的理论基础,另一方面在于其具有极高的实践场景,在业务中经常遇到棘手的fullGC需要处理。
3.1 请谈谈在老年代和新生代层面的GC
3.1.1 Minor GC
在新生代空间(包括 Eden 和 Survivor 区域)回收内存被称为 Minor GC。这一定义既清晰又易于理解。但是,当发生Minor GC事件的时候,有一些有趣的地方需要注意到:
- 当 JVM 无法为一个新的对象分配空间时会触发 Minor GC,比如当 Eden 区满了。所以分配率越高,越频繁执行 Minor GC
- 内存池被填满的时候,其中的内容全部会被复制,指针会从 0 开始跟踪空闲内存。Eden 和 Survivor 区进行了标记和复制操作,取代了经典的标记、扫描、压缩、清理操作。所以 Eden 和 Survivor 区不存在内存碎片。写指针总是停留在所使用内存池的顶部
- 执行 Minor GC 操作时,不会影响到永久代。从永久代到年轻代的引用被当成 GC roots,从年轻代到永久代的引用在标记阶段被直接忽略掉
- 质疑常规的认知,所有的 Minor GC 都会触发"全世界的暂停(stop-the-world)",停止应用程序的线程。对于大部分应用程序,停顿导致的延迟都是可以忽略不计的。其中的真相就是,大部分 Eden 区中的对象都能被认为是垃圾,永远也不会被复制到 Survivor 区或者老年代空间。如果正好相反,Eden 区大部分新生对象不符合 GC 条件,Minor GC 执行时暂停的时间将会长很多。
3.1.2 Full GC
Full GC 的触发条件:
- 调用 System.gc()
- 老年代空间不足,常见场景为前文所讲的大对象直接进入老年代、长期存活的对象进入老年代等
- 空间分配担保失败,使用复制算法的 Minor GC 需要老年代的内存空间作担保,如果担保失败会执行一次 Full GC
- JDK 1.7 及以前的永久代空间不足
这里补充一下与 System.gc() 相关的内容:
当调用System.gc()的时候,其实并不会马上进行垃圾回收,甚至不一定会执行垃圾回收。
public static void gc() {
boolean shouldRunGC;
synchronized(lock) {
shouldRunGC = justRanFinalization;
if (shouldRunGC) {
justRanFinalization = false;
} else {
runGC = true;
}
}
if (shouldRunGC) {
Runtime.getRuntime().gc();
}
}
也就是 justRanFinalization=true 的时候才会执行。查找发现当调用 runFinalization()的时候 justRanFinalization 变为true。
public static void runFinalization() {
boolean shouldRunGC;
synchronized(lock) {
shouldRunGC = runGC;
runGC = false;
}
if (shouldRunGC) {
Runtime.getRuntime().gc();
}
Runtime.getRuntime().runFinalization();
synchronized(lock) {
justRanFinalization = true;
}
}
直接调用 System.gc() 只会把这次 gc 请求记录下来,等到 runFinalization=true 的时候才会先去执行 GC,runFinalization=true 之后会在允许一次 system.gc()。之后在 call System.gc() 还会重复上面的行为。所以 System.gc() 要跟 System.runFinalization() 一起搭配使用:
static void gcAndFinalize() {
final VMRuntime runtime = VMRuntime.getRuntime();
System.gc();
runtime.runFinalizationSync();
System.gc();
}
java.lang.System.gc()只是java.lang.Runtime.getRuntime().gc()的简写,两者的行为没有任何不同。唯一的区别就是System.gc()写起来比Runtime.getRuntime().gc()简单点。GC本身是会周期性的自动运行的,由JVM决定运行的时机,而且现在的版本有多种更智能的模式可以选择,还会根据运行的机器自动去做选择,就算真的有性能上的需求,也应该去对GC的运行机制进行微调,而不是通过使用这个命令来实现性能的优化。
3.1.3 Major GC 与 Full GC 的区别
- Major GC 是清理老年代
- Full GC 是清理整个堆空间—包括年轻代和老年代
- 执行 CMS GC 的过程中同时有对象要放入老年代,而此时老年代空间不足
很不幸,实际上它还有点复杂且令人困惑。首先,许多 Major GC 是由 Minor GC 触发的,所以很多情况下将这两种 GC 分离是不太可能的。另一方面,许多现代垃圾收集机制会清理部分永久代空间,所以使用“cleaning”一词只是部分正确。
3.2 请谈谈你对GC分类标准的理解
针对HotSpot VM的实现,它里面的GC其实准确分类只有两大种:
- Partial GC:并不收集整个GC堆的模式
-
- Young GC:只收集新生代的GC
-
- Old GC:只收集老年代的GC。只有CMS的concurrent collection是这个模式
-
- Mixed GC:收集整个新生代以及部分老年代的GC。只有G1有这个模式
- Full GC:收集整个堆,包括新生代、老年代、perm gen(如果存在的话)等所有部分的模式
Major GC 通常是跟 Full GC 是等价的,收集整个 GC 堆。但因为 HotSpot VM 发展了这么多年,外界对各种名词的解读已经完全混乱了,当有人说“major GC”的时候一定要问清楚他想要指的是上面的 Full GC 还是 Old GC。
最简单的分代式 GC 策略,按 HotSpot VM 的 serial GC 的实现来看,触发条件是:
- young GC:当新生代 中的eden区分配满的时候触发。注意 young GC 中有部分存活对象会晋升到老年代 ,所以 young GC 后 老年代 的占用量通常会有所升高。
- full GC:当准备要触发一次 young GC 时,如果发现之前 young GC 的平均晋升大小比目前 老
剩余60%内容,订阅专栏后可继续查看/也可单篇购买
“挨踢”行业行情日益严峻,企业招聘的门槛也随之越来越高,大厂hc少之又少。 庞大的知识体系下,不知道学什么、怎么学? 面试高频考点是什么、怎么回答才能得到面试官的青睐? 作为后端求职者,在Java的道路上越走越宽。 本专刊则针对Java面试考点上,精讲JVM知识点,为大家的大厂求职路保驾护航! 针对如今校招痛点,深入详解JVM知识考点,列出高频真题并详细解答!探索JVM精髓!


曼迪匹艾公司福利 152人发布