常见的 JVM 面试题分析

点击下方关注我,然后右上角点击...“设为星标”,就能第一时间收到更新推送啦~~~

8 个常见的 JVM 面试题分析。

1、什么时候会触发垃圾回收,Minor GC 和 Full GC 的区别?

垃圾回收 GC 是由 JVM 根据运行情况自动完成的,触发垃圾回收的时机是不确定的,当然我们可以通过调用System.gc()方法通知 JVM 进行垃圾回收,但是什么时候回收还是由 JVM 决定,但是不建议手动调用System.gc()方法。一般以下几种情况会发生垃圾回收

  • 当新生代(Eden 区或者 S 区)空间不够用时,触发 Minor GC

  • 当老年代(Old 区)空间不够用时,触发 Major GC,进而可能触发 Full GC

  • 当方法区(Metaspace 区)空间不够用时,触发 Full GC

Minor GC 指的是新生代的 GC,也就是 Young GC, Major GC 指的是老年代的 GC,也就是 Old GC,Full GC 等于 Young GC+Old GC+Metaspace GC。

2、什么样的对象是垃圾对象,垃圾对象一定会被回收吗?

在可达性分析法中不可达的对象是垃圾对象,但是垃圾对象不一定就会被回收。

一个对象在可达性分析法中如果判定不可达会被初始标记,初始标记之后还需要进行一次筛选分析(就是看该对象是否需要执行 finalize() 方法)。

筛选分析过程:如果对象没有覆盖 finalize()  方法或者 finalize()  方法已经被虚拟机调用过,那么定该对象不需要执行 finalize()  方法;如果被判定为需要执行 finalize()  方法的对象,那么就会被放在一个队列中进行再次标记。

再次标记过程:再次标记时如果这个对象在引用链上存在任何一个对象与之关联那么就会被激活,否则就会被进行垃圾回收。

3、为什么需要 Survivor 区,只有 Eden 区可以吗?为什么需要两个 Survivor 区?

如果没有 Survivor 区,Eden 区每进行一次 Minor GC,并且没有年龄限制的话,存活的对象就会被送到老年代。这样一来,老年代很快就会空间用完,触发 Major GC,老年代的内存空间远大于新生代,进行一次 Full GC消耗的时间比 Minor GC长得多。

频繁的 Full GC 消耗的时间很长,会影响应用程序的执行和响应速度,一般会想到可以增加老年代空间降低 Full GC 频率,或者减少老年代空间降低 Full GC所需时间。

如果对老年代的空间进行增加,那么更多存活对象才能填满老年代,这样虽然降低了 Full GC 频率,但是随着老年代空间加大,一旦发生 Full GC,执行垃圾清理所需要的时间更长。如果减少老年代空间,虽然 Full GC 所需时间减少了,但是老年代很快被存活对象填满,从而 Full GC 的频率增加了。

所以 Survivor 区存在的意义就是发生 Minor GC 时把 Eden 区存活的对象放入 Survivor 区,这样可以减少被送到老年代的对象,进而减少 Full GC 的发生,Survivor 区设置的年龄阈值默认 15,也就是说只有经历 16 次 Minor GC 还能在新生代中存活的对象,才会被送到老年代,从而大大减少了  Full GC 的触发(垃圾回收要尽量减少垃圾回收的频率)。

两个 Survivor 区最大的好处就是解决了 Young 区空间碎片化的问题。假设现在只有一个 Survivor 区,新建的对象在 Eden 区中,一旦 Eden 区满了,触发一次 Minor GC,Eden 区中的存活对象就会被移动到 Survivor 区,这样继续循环下去,当下一次 Eden 区满了的时候,再次触发 Minor GC,这时 Survivor 区可能有一些存活对象,导致 Survivor 区内存是不连续的,也就导致了 Survivor 区内存碎片化的问题。再增加一个 Survivor 区,就能保证永远有一个 Survivor 区是空的,另一个非空的 Survivor 区无碎片。

4、如果 Full GC 频繁怎么办?

Full GC 频繁说明老年代涌入了大量对象,这个时候就应该检查下 JVM 的参数配置,默认 Old:Young=2:1,很有可能是新生代设置的太小了,导致很多应该在 Minor GC 阶段就清理的对象留到了老年代,想办法减少 Full GC 的次数,可以适当增加 Young 区的大小来解决。

新生代可以分为 Eden 区、S0 区、S1 区,正常的对象分配都是在 Eden 区完成的,如果 Eden 区空间不够了,会触发一次 Minor GC,存活的对象放在 S0 或 S1 中。每执行一次 Minor GC,存活的对象会不断地从 S0 迁到 S1,再从 S1 迁到S0,这个过程经过几次之后,当对象年龄为 15 时,如果对象还是存活的就会把新生代的对象放入老年代中(年龄默认是 15 岁,可以通过参数 -XX:MaxTenuringThreshold 来设置)。如果新生代大小设置的太小,就会导致非常频繁的 Minor GC, S0 和 S1 来回切换的速度加快,导致本身应该在 Minor GC 就清理出去的对象跑到了老年代,从而导致 Full GC 的次数频繁执行。

5、只有 Full GC 才会触发 STW (stop the world)吗?

STW (stop the world)指的是 GC 执行过程中,可能会暂停应用程序的执行,这个停顿期间会导致所有应用程序的线程都暂停执行,没有任何响应, 有点像卡死的感觉,这个停顿叫做 STW。

这种现象一般是由 GC 引起的,不管是什么 Full GC 还是 Young GC,都会有 STW,只是暂停时间的长短不一样,垃圾收集器的演进过程就是为了更好地缩短这个停顿时间。

6、CMS 与 G1 垃圾收集器的区别?

CMS 和 G1 都是并发回收收集器,但是 CMS 只用于老年代的回收,而 G1 可用于新生代和老年代的回收。

CMS 使用标记-清理算法实现,G1 引入了  Region 内存布局方式,使用标记-整理算法实现,整体减少了垃圾碎片的产生,CMS 和 G1 都在追求最短的用户线程停顿时间为目标,但是 G1 比 CMS 先进的是可以指定垃圾回收的停顿时间。

7、内存泄漏和内存溢出的区别?

内存泄漏(memory leak):是指程序在申请内存后,无法释放已申请的内存空间,导致对象无法得到及时的回收,持续占用内存空间,从而造成内存空间的浪费,内存泄漏的堆积最终会导致内存溢出。

内存溢出(out of memory):内存溢出就是我们常说的 OOM,它是指程序在申请内存时,没有足够的内存空间可以为其分配,就会内存溢出;另外内存溢出也有可能是大对象导致的。

8、方法区垃圾回收的内容是什么?

方法区主要存储的是类信息,那么方法区垃圾回收主要是回收“无用的类”和“无用的常量”。

如何判断“无用的类” :

  • 该类所有的实例都已经被回收,也就是堆里面不再有该类任何实例对象

  • 加载该类的 ClassLoader 已经被回收

  • 该类对应的 java.lang.Class 对象没有在任何地方被引用,也就是无法通过反射访问该类的方法

一个类需要同时满足上面 3 个条件才能算是“无用的类” ,也就是说虚拟机可以对其进行垃圾回收。

运行时常量池主要存储的是常量信息,那么运行时常量池回收主要是回收“无用的常量”。

如何判断“无用的常量”:

  • 假如在常量池中存在字符串常量"helloWorld" ,如果当前没有任何 String 对象引用该字符串常量的话,就说明常量"helloWorld"就是“无用的常量”,也就是说虚拟机可以对其进行垃圾回收,"helloWorld"就会被虚拟机清理出常量池。

JVM 十三个应知应会知识点全部结束