写点什么

架构师训练营第 1 期 - 第 9 周课后练习

用户头像
Anyou Liu
关注
发布于: 2020 年 11 月 22 日
  1. 请简述 JVM 垃圾回收原理。

答:

  1. JVM 的对象都分配在堆里面,可以通过虚拟机参数-xms 指定堆的初始大小,参数-xmx 指定堆的最大值,JVM 会定期进行垃圾回收,回收的就是堆里面的对象。垃圾回收的机制是怎么样的?

  • 堆里面会划分成新生代和老年代,新生代和老年代的比率通过虚拟机参数-XX:NewRatio 指定,默认是 2,有公式:新生代大小=堆大小 / (1+NewRatio),默认情况下新生代大小就是堆的 33%,新生代里面又分成 survivor0、survivor1 和 eden 区,survivor 区的初始大小通过虚拟机参数-XX:InitialSurvivorRatio 指定,默认是 8,有公式:survivor 大小=新生代大小 / (InitialSurvivorRatio + 2),默认情况下初始 survivor 大小就是新生代大小的 10%,JVM 会动态调整 survivor 的大小,通过-XX:MinSurvivorRatio 设定上限

  • 对象首先会被分配到新生代的 Eden 区,当 Eden 区满的时候,JVM 会触发一次垃圾回收,因为是在新生代的垃圾回收,所以也叫 Young GC。所有垃圾对象所占用的空间都会被释放掉,非垃圾对象会被复制到 Survivor0 区。那么如何判断是垃圾对象呢?JVM 通过可达性分析来判断对象是否是垃圾对象。可代行分析是指从 GC root 出发,如果对象没有在引用链上,那么对象就是不可达的,否则就是可达的。可达性分析解决引用分析中循环引用的问题,比如下图右边这种是不可达的,会被垃圾回收。可用作 GC root 的对象有:虚拟机栈中的对象、静态变量或常量引用的对象、本地方法栈中的对象。


  • 一次 Young GC 会释放 Eden 区所有的空间,还被 GC root 引用的对象被复制到 Survivor0 区。当下一次 Eden 区满,并触发 Young GC 的时候,Survivor0 中还在使用的对象和 Eden 区中还在使用的对象就被复制到 Survivor1 区中。下一次 Young GC 的时候,还在使用的对象又会被复制到 Survivor0 中,这样来回复制多次之后,当到达一定的次数对象还在使用的话,对象会被复制到老年代,也叫做晋升。虚拟机参数-XX:InitialTenuringThreshold 可以指定初始晋升阈值,还可以指定最大晋升阈值-XX:MaxTenuringThreshold,即使指定了初始晋升阈值,JVM 也会持续的计算,并找到合理的阈值,最终会在 1 和最大晋升阈值之间选择一个合理的值。

  • 当越来越多的对象晋升到老年代并占满老年代时,JVM 会触发一次 Full GC。Full GC 的时候,老年代的垃圾对象会被回收,Eden 区、Survivor0、Survivor1 区的空间会释放并把还在使用的对象移到老年代。Young GC 和 Full GC 的时候,JVM 会暂停应用程序的线程,无法响应任何请求,也叫 Stop the world,但是垃圾回收的线程还是在正常工作。一般 Full GC 比较耗时,应用程序应该避免经常性的 Full GC,Stop the world 会导致应用不可用,影响系统可用性。

  • 具体的垃圾回收算法有:串行回收器、并行回收器、CMS 回收器、G1 回收器。串行回收器就是单线程进行垃圾回收,效率低下,现在基本不用。并行回收器就是多线程进行垃圾回收,比串行性能高,但是每次都是垃圾回收的时候再检查对象可达性,性能有待提高。CMS 回收器就是解决这个问题,CMS 利用了标记清除算法,有初始标记、再标记、预清理、重新标记、清除、并发重置阶段,本质上是为了提前清除垃圾对象,减小垃圾回收的压力,减少 GC 的次数。G1 回收器默认情况下是把堆的空间分成 2048 的大小相等的分区,每个分区隶属于新生代或者老年代,并不要求所有新生代的分区或者老年代的分区是连续的,控制的垃圾回收的内存区域粒度更细,提高了垃圾回收的效率,是 Java 9 之后默认的垃圾回收器。

用户头像

Anyou Liu

关注

还未添加个人签名 2019.05.24 加入

还未添加个人简介

评论

发布
暂无评论
架构师训练营第 1 期 - 第 9 周课后练习