(面试题)有关JVM垃圾回收机制的那些算法

三种垃圾回收算法

  • 标记-清除(年老代)
  • 标记-整理(即标记-压缩)(年老代)
  • 复制(年轻代)

1、标记-清除算法

原理

  • 从根集合节点进行扫描,标记出所有的存活对象,最后扫描整个内存空间并清除没有标记的对象(即死亡对象)

适用场合

  • 存活对象较多的情况下比较高效
  • 适用于年老代(即旧生代)

缺点

  • 容易产生内存碎片,再来一个比较大的对象时(典型情况:该对象的大小大于空闲表中的每一块儿大小但是小于其中两块儿的和),会提前触发垃圾回收
  • 扫描了整个空间两次(第一次:标记存活对象;第二次:清除没有标记的对象)

注意:

  • 在该情况下,内存不规整,对象的内存分配采用"空闲列表法",

2、标记整理算法

原理:

  • 从根集合节点进行扫描,标记出所有的存活对象,最后扫描整个内存空间并清除没有标记的对象(即死亡对象)(可以发现前边这些就是标记-清除算法的原理),清除完之后,将所有的存活对象左移到一起。

适用场合:

  • 用于年老代(即旧生代)

缺点:

  • 需要移动对象,若对象非常多而且标记回收后的内存非常不完整,可能移动这个动作也会耗费一定时间
  • 扫描了整个空间两次(第一次:标记存活对象;第二次:清除没有标记的对象)

优点:

  • 不会产生内存碎片

注意:

  • 在该情况下,内存规整,对象的内存分配采用"指针碰撞法"。

3、复制算法

原理:

  • 从根集合节点进行扫描,标记出所有的存活对象,并将这些存活的对象复制到一块儿新的内存(图中下边的那一块儿内存)上去,之后将原来的那一块儿内存(图中上边的那一块儿内存)全部回收掉

适用场合:

  • 存活对象较少的情况下比较高效
  • 扫描了整个空间一次(标记存活对象并复制移动)
  • 适用于年轻代(即新生代):基本上98%的对象是"朝生夕死"的,存活下来的会很少

缺点:

  • 需要一块儿空的内存空间
  • 需要复制移动对象

注意:

  • 在该情况下,内存规整,对象的内存分配采用"指针碰撞法"。
  • 以空间换时间:通过一块儿空内存的使用,减少了一次扫描。

 

分代收集算法

分代收集算法理论来源于统计学。IBM公司的专门研究发现,对象的生存周期总体可分为三种:新生代、老年代和永久代。因此可以根据各个年代的特点采用适当的垃圾回收算法。比如新生代的对象在每次垃圾时都会有大量的对象死去,只有很少一部分存活,那就可以选择标记-复制算法。另外,在新生代中每次死亡对象约占98%,那么在标记-复制算法中就不需要按照1:1的比例来划分内存区域,而是将新生代细分为了一块较大的Eden和两块较小的Survivor区域,HotSpot中默认这两块区域的大小比例为8:2。每次新生代可用区域为Eden加上其中一块Survivor区域,共90%的内存空间,这样就只有10%的内存空间处在被闲置状态。在进行垃圾回收时,存活的对象被转移到原本处在“空闲的”Eden区域。如果某次垃圾回收后,存活对象所占空间远大于这10%的内存空间时,也就是Survivor空间不够用时,需要额外的空间来担保,通常是将这些对象转移到老年代。对于老年代来说,大部分对象都处在存活状态。同时,如果一个大对象要在该区域进行分配,而内存空间又不足,那么在没有外部内存空间担保的情况下,就必须选用标记-清除或者标记-整理算法来进行垃圾回收了。

总而言之,分代收集只是根据对象生存周期的不同来选择不同的算法,其本身并没有任何新思想。


垃圾回收机制

年轻代分为Eden区和survivor区(两块儿:from和to),且Eden:from:to==8:1:1

1)新产生的对象优先分配在Eden区(除非配置了-XX:PretenureSizeThreshold,大于该值的对象会直接进入年老代);

2)当Eden区满了或放不下了,这时候其中存活的对象会复制到from区(这里,需要注意的是,如果存活下来的对象from区都放不下,则这些存活下来的对象全部进入年老代),之后Eden区的内存全部回收掉;注意:如果是Eden区没有满,但是来了一个小对象Eden区放不下,这时候Eden区存活对象复制到from区后,清空Eden区,之后刚才的小对象再进入Eden区

3)之后产生的对象继续分配在Eden区,当Eden区又满了或放不下了,这时候将会把Eden区和from区存活下来的对象复制到to区(同理,如果存活下来的对象to区都放不下,则这些存活下来的对象全部进入年老代),之后回收掉Eden区和from区的所有内存;

4)如上这样,会有很多对象会被复制很多次(每复制一次,对象的年龄就+1),默认情况下,当对象被复制了15次(这个次数可以通过:-XX:MaxTenuringThreshold来配置),就会进入年老代了

5)当年老代满了或者存放不下将要进入年老代的存活对象的时候,就会发生一次Full GC(这个是我们最需要减少的,因为耗时很严重)


总结:

  • 年轻代:复制算法
  • 年老代:标记-清除或标记-整理(前者相较于后者会快一些但是会产生内存碎片,后者相较于前者不会产生内存碎片但是由于要移动存活对象所以会慢一些)
  • 以上这种年轻代与年老代分别采用不同回收算法的方式称为"分代收集算法",这也是当下企业使用的一种方式
  • 每一种算法都会有很多不同的垃圾回收器去实现,在实际使用中,根据自己的业务特点做出选择就好

猜你喜欢

转载自blog.csdn.net/weixin_42230885/article/details/84800708