最近阿粉的小学妹,给阿粉留言,说面试官不按套路出牌,问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 的知识,这样大家就能保证在面试的时候被面试官换个问法就不会的情况了。