• 临近秋招,备战暑期实习,祝大家每天进步亿点点!Day08
  • 有粉丝大佬要求更新有难度的,所以本篇总结的是 JVM 相关的面试题,后续会每日更新~

(Java实习生)每日10道面试题打卡——JVM篇 (三)_原力计划


1、Java中的异常体系

Java 冲所有异常都来自顶级父类 Throwable

Throwable 下有两个子类 ExceptionError,用于表示程序出现了不正常的情况。区别在于:


  • Error 是程序错误,通常为虚拟机相关错误,如系统崩溃内存不足堆栈溢出等,编译器不会对这类错误进行检测,JAVA 应用程序也不应对这类错误进行捕获,一旦这类错误发生,通常应用程序会被终止,仅靠应用程序本身无法恢复
  • Exception 是程序异常,是可以在应用程序中进行捕获并处理的,是一种设计或实现问题,也就是说,它表示如果程序运行正常,从不会发生的情况。

    • Exception 又分为两个部分:运行时异常(RunTimeException)和检查异常(CheckedException)。
    • RunTimeException 通常发生在程序运行过程中,会导致当前程序的线程执行失败(不会影响其他线程,例如控空指针异常~)。
    • CheckedException 通常发生在程序编译的过程中,会导致编译不通过(也可以叫做编译时异常)。



2、JVM中的永久代中会发生垃圾回收吗?

会发生垃圾回收,如果永久代满了或者是超过了临界值,会触发完全垃圾回收( Full GC)。


3、简述分代垃圾回收算法是的执行流程?

  • 分代收集算法 :这种算法是把 Java 堆分为新生代老年代,新生代默认的空间占比总空间的 1/3,老生代的默认占比是 2/3。根据不同年代的特点采用最适当的收集算法。

    • 新生代中,每次垃圾收集时都发现有大批对象死去,只有少量存活,那就选用复制算法,只需要付出少量存活对象的复制成本就可以完成收集。
      • 新生代里有 3 个分区:伊甸园、To 幸存区、From 幸存区,它们的默认占比是 ​​8:1:1​​。
    • 老年代中,因为对象存活率高、没有额外空间对它进行分配担保,就必须使用标记—清理或者标记—整理算法来进行回收。

下面来逐步介绍一下分代收集算法的流程:

  • 长时间使用的对象放在老年代中(长时间回收一次,回收花费时间久),用完即可丢弃的对象放在新生代中(频繁需要回收,回收速度相对较快):

(Java实习生)每日10道面试题打卡——JVM篇 (三)_老年代_02

  • 新创建的对象都被放在了新生代的伊甸园中:

(Java实习生)每日10道面试题打卡——JVM篇 (三)_java_03

(Java实习生)每日10道面试题打卡——JVM篇 (三)_java_04

  • 当伊甸园中的内存不足时,就会进行一次垃圾回收,这时的回收叫做 Minor GC (Young GC):

Minor GC 会将伊甸园和幸存区FROM仍需要存活的对象复制到 幸存区 TO中, 并让其寿命加1,再交换FROM和TO

(Java实习生)每日10道面试题打卡——JVM篇 (三)_原力计划_05

  • 伊甸园中不需要存活的对象将其清除:

(Java实习生)每日10道面试题打卡——JVM篇 (三)_老年代_06

  • 交换FROM和TO

(Java实习生)每日10道面试题打卡——JVM篇 (三)_原力计划_07

  • 同理,继续向伊甸园新增对象,如果满了,则进行第二次 Minor GC

流程相同,仍需要存活的对象寿命​​+1​​:(下图中 FROM 中寿命为1的对象是新从伊甸园复制过来的,而不是原来幸存区 FROM 中的寿命为1的对象,这里只是静态图片不好展示,只能用文字描述了)

(Java实习生)每日10道面试题打卡——JVM篇 (三)_垃圾回收_08

再次创建对象,若新生代的伊甸园又满了,则会再次触发 Minor GC(会触发 Stop The World, 暂停其他用户线程,只让垃圾回收线程工作),这时不仅会回收伊甸园中的垃圾,还会回收幸存区中的垃圾,再将活跃对象复制到幸存区TO中。回收以后会交换两个幸存区,并让幸存区中的对象寿命加1

  • 如果幸存区中的对象的寿命超过某个阈值(最大为 154 bit),就会被放入老年代中:

(Java实习生)每日10道面试题打卡——JVM篇 (三)_jvm_09

  • 如果新生代老年代中的内存都满了,就会先触发 Minor Gc,再触发 Full GC,扫描新生代和老年代中所有不再使用的对象并回收:

(Java实习生)每日10道面试题打卡——JVM篇 (三)_java_10


分代收集算法流程小结:



  • 新创建的对象首先会被分配在伊甸园区域。
  • 新生代空间不足时,触发 Minor GC,伊甸园和 FROM 幸存区需要存活的对象会被 COPY 到 TO 幸存区中,存活的对象寿命​​+1​​,并且交换 FROMTO
  • Young GC 会引发 Stop The World:暂停其他用户的线程,等待垃圾回收结束后,用户线程才可以恢复执行。
  • 当对象寿命超过阈值​​15​​时,会晋升至老年代。
  • 如果新生代、老年代中的内存都满了,就会先触发 Minor GC,再触发 Full GC,扫描新生代和老年代中所有不再使用的对象并回收。


4、新生代中,伊甸园区和FROM、To幸存区的默认比例是?

在HotSpot虚拟机中,Eden区和Survivor区的默认比例为​​8:1:1​​​,即​​-XX:SurvivorRatio=8​​​,其中Survivor分为From Survivor和ToSurvivor,因此Eden此时占新生代空间的​​80%​​。


5、HotSpot GC的分类?


  • Young GC/Minor GC:只收集新生代的GC。
  • Full GC/Major GC:收集整个GC堆,包括新生代、老年代、永久代(如果存在的话)等所有部分。


6、HotSpot GC的触发条件?

Young GC:当新生代中的Eden区没有足够空间进行分配时会触发 Young GC

Full GC


  • 当准备要触发一次 Young GC 时,如果发现统计数据说之前 Young GC 的平均晋升大小比目前老年代剩余的空间大,则不会触发 Young GC 而是转为触发 Full GC。(通常情况)
  • 如果有永久代的话,在永久代需要分配空间但已经没有足够空间时,也要触发一次 Full GC
  • ​System.gc()​​ 默认也是触发 Full GC


7、什么情况下新生代对象会晋升到老年代?


  • 新创建的对象会被放入伊甸园中,当伊甸园中存放对象过多,导致内存不够再放入新对象时,就会触发 Young GC
  • 每一次 Young GC 都会将伊甸园和幸存区FROM仍需要存活的对象复制到 幸存区 TO中, 并让其寿命加1,再交换FROM和TO
  • 如果幸存区中的对象的寿命超过某个阈值(最大为 154 bit),就会被放入老年代中。
  • 如果新生代、老年代中的内存都满了,就会先触发 Young GC,再触发 Full GC,扫描新生代和老年代中所有不再使用的对象并回收。


8、发生Young GC的时候需要扫描老年代的对象吗?

不会

  • 在分代收集中,新生代的规模一般都比老年代要小许多,新生代的收集也比老年代要频繁许多,如果回收新生代时也不得不同时扫描老年代的话,那么Young GC的效率可能下降不少,显然是不可能区扫描老年代的。

9、jvm 如何确定一个类?(JVM如何判断类相同)

在Java中,一个类的全名(​​包名+类名​​)作为其标识,但在JVM中,一个类用其 全名 + 类加载器作为唯一标识,不同类加载器加载的类置于不同的命名空间中,这叫做类加载器隔离。


10、new Object 和包装类型,一个对象最少需要几个字节?

  • 一个对象最少 16 字节。