面试官问我JVM的GC分代收集算法为什么这么设计

最近阿粉的小学妹,给阿粉留言,说面试官不按套路出牌,问JVM的相关知识的时候,不问有什么GC算法,而是问我为什么这么设计,让学妹很懵圈,阿粉就差给小学妹的脑壳敲破了,面试官这么问,只是考验你,知其然,知其所以然么?今天阿粉就来简单的说说这个。

JVM 的垃圾回收机制

我们先来说说这个回收机制的算法都有哪些,如图所示。

目前面试比较常问的垃圾回收算法就是这几种,我们分开来说,最后说说分代收集为什么选择不同的算法来实现。

标记清除算法 Mark-Sweep

我们都知道,标记清除算法,是垃圾回收算法当中算是最基础的算法了,因为标记算法就只有两个阶段,

  • 阶段一 标记
  • 阶段二 清除

标记的是什么内容呢?

标记的都是所有的需要被回收的对象,当执行到清除阶段的时候,就会直接把这些标记的对象给完整的清除掉。

如果是这样的话,那么就会出现了一个问题,大家看,如果灰色的是我们的内存空间,然后我们把需要把被回收的对象清除的话,我们不能保证这个被回收的对象,一定会是连续排在一起的,就比如所有需要被回收的对象,都排在最上面的内存空间中,这个是不太可能的,所以,执行完清除之后,这些未使用的内存空间,就成了一个不连续的内存空间。

标记清除算法,最大的弊端出现了,碎片化就非常的严重,如果有大对象想要存入,而内存中出现没有连续空间的话,那他就没有可用空间保存了。

为了解决碎片化严重的这种情况,就有了下面的这种垃圾回收算法。

复制算法(copying)

为了解决这个内存碎片化严重的问题,按内存容量将内存划分为等大小的两块。每次只使用其中一块,当这一块内存满后将尚存活的对象复制到另一块上去,把已使用的内存清掉。

实际上,这种方式大家看起来,有没有什么问题呢?解决了碎片化严重的情况,但是他把内存空间,直接划分成了相等的两块,如果我们目前需要被回收的对象比较少,存活的对象比较多的话,那么这种复制算法的效率,真的是有点低了。

那么有没有一个折中的呢?

这就出现了另外一个算法,

标记整理算法(Mark-Compact)

这种算法比较特殊了,标记阶段和 Mark-Sweep 算法相同,但是整理的时候,就不一样了,标记后不是清理对象,而是将存活对象移向内存的一端。然后清除端边界外的对象。也就是,有可能是存活对象被移到左边,然后右边是需要被清理的对象,

这样既能保证了内存空间是连续的,而且还能让效率提升。

那么我们可以回归这个标题了,GC分代收集,为什么这么设计。

分代

这个就挺好理解的,毕竟都知道一个共同的知识点,那就是 GC堆内存分为了老年代和新生代。

如果要选择算法,那么一定得从他们的本质去入手。

老年代:存活数量多,需要被处理的对象少

新生代:存活数量少,需要被处理的对象多

这种从本质的区别就划分出来了,一个存活对象多,一个存活对象少,一个需要被清理的对象多,一个需要被清理的对象少。

复制算法因为每次复制的都是存活的对象,而新生代的存活对象都是比较少的,所以这个时候就可以采用复制算法来实现。

也就是说,新生代中划分出来的大Eden 区 和两个 Survivor区,每次使用的时候都是 Eden 区和其中的一块 Survivor 区,当进行回收时,将该两块空间中还存活的对象复制到另一块 Survivor 区中。

所以因为新生代的这种特性,所以使用复制算法。

而老年代因为每次只回收少量对象,因而采用 Mark-Compact 算法。

这就是为什么面试的时候,面试官会问你为什么GC分代收集时选择不同算法的原因。

JVM的垃圾收集器

一般面试很多都是执着于去问垃圾回收机制和算法,很少有涉及到JVM的垃圾收集器的,阿粉今天稍微科普一下这个小知识。

Serial 收集器(新生代)

最早的收集器

采用复制算法,暂停所有用户线程,

特点是简单高效并且是单线程,但是容易导致全局停顿,就是我们经常所说的 STW(全局暂停)。

STW:

全局停顿,Java 代码停止运行,native 代码继续运行,但不能与 JVM 进行交互

ParNew收集器(新生代)

实际上属于 Serial 收集器 的升级版,从单线程变成了多线程,算法一样,也是暂停所有用户线程。

主要用来搭配 CMS 收集器一起使用。

Parallel Scavenge收集器(新生代)

吞吐量收集器,这个收集器关注的是吞吐量

在 JVM 中有参数可以配置

  • -XX:MaxGCPauseMillis:控制最大的垃圾收集停顿时间
  • -XX:GCTimeRatio:设置吞吐量的大小,取值 0-100, 系统花费不超过 1/(1+n) 的时间用于垃圾收集

Serial Old 收集器(老年代)

老年代的收集器,采用标记-整理算法

CMS 收集器(老年代)

算法采用标记-清除算法实现,

一般这个面试问的可能比较多,因为它属于并发的收集器,因为它并不会像前面说的那些收集器一样,会直接导致所有用户线程停止,直到清除结束,而是在标记过程中会有短暂的停止。

而是先进行初始标记,然后进行并发标记,修正并发标记用以进行重新标记,最后进行并发清除。

G1 收集器

G1(Garbage-First)收集器将堆内存分割成不同的区域,然后并发的对其进行垃圾回收。G1收集器的设计目标是取代CMS收集器,它同CMS相比,不会产生大量内存碎片,并可以添加预测机制,用户可以指定期望停顿时间(可通过配置-XX:MaxGCPauseMills=n最大停顿时间)

收集演示图:

说这些,最重要的却是,如何选择合适的垃圾收集器

组合选择:

  • 单CPU或小内存,单机程序 -XX:+UseSerialGC
  • 多CPU,需要最大吞吐量,如后台计算型应用 -XX:+UseParallelGC或者 -XX:+UseParallelOldGC
  • 多CPU,追求低停顿时间,需快速响应如互联网应用 -XX:+UseConcMarkSweepGC -XX:+ParNewGC

以上就是阿粉给大家带来的关于面试中的JVM 的一些小小的知识点了,有兴趣的可以继续深入了解关于 JVM 的知识,这样大家就能保证在面试的时候被面试官换个问法就不会的情况了。

猜你喜欢

转载自blog.csdn.net/java_beautiful/article/details/125872295