JVM四:垃圾收集算法+HotSPot的算法实现

垃圾收集算法分为标记清除,复制算法,标记整理,下面我们来看看这三种基础的收集算法。

标记-清除算法:分为两个阶段,标记和清除阶段。

   首先标记出所有需要回收的对象,在标记完成后统一回收所有被标记的对象。标记过程与上一章的过程一样。这种标记算法的由两个不足;一是效率问题,标记和清除两个阶段的效率都不高;二是会产生空间碎片,空间碎片太多可能会导致以后在程序运行时无法分配较大的对象。导致再一次触发一次垃圾回收。

复制算法

   复制算法是将内存分为两块,每次只使用其中的一块,当这块的内存用完或者执行垃圾回收算法时将存活的对象复制到另一块再把这块内存完全清除,这样依次交替使用。这样不会产生空间碎片,运行也高效,就是只能利用内存的一半,耗费的资源高。

   因为这种算法缺点是耗费内存高,故适用于产生存活对象较低的进行收集,因此虚拟机将这种垃圾收集算法应用在新生带(Eden区域和Survivor),其中虚拟机默认的情况是Eden区域占八份,Survivor占一份,还要10%则用来复制,这是通过大部分软件统计后,当然如果10%无法存放存活对象,则会向永久代进行借内存,这也就是分配担保。

标记整理:分为三个阶段,标记-整理-清除

  其中标记阶段与清除阶段与标记清楚算法的两个阶段一样,只不过在标记之后会将标记的对象进行整理(让存活的对象向一端进行移动),在将另一块内存清除,这样就避免的空间碎片的产生。这是基于老年代的特点提出来的。

以上就是三种基本的垃圾收集算法:现在的商业虚拟机都是采用分代收集算法,这种算法没有什么新的思想,只是根据对象存活周期将内存划分为几块。一般是把堆划分为新生代和老年代(方法区中也有),根据各个代的特点采用特定的算法进行垃圾回收。比如新生代因为存活率低采用复制算法,老年代因为没有额外的空间担保一般采用标记整理算法。

了解对象的存活判断与回收算法,下面来看一下HotSpot虚拟机具体是怎么样执行的(保证效率的情况)

   解决标记阶段的问题一:首先三个基础算法的都有标记阶段,一般标记阶段都是通过可达性分析区判断对象的存活,这个就需要虚拟机在分析时所有线程停止(城为STW),虚拟机在具体的执行过程中都是通过把每个对象做个GCRoot进行分析,这样就需要堆所有的对象进行分析,显然这样效率低导致STW的停顿时间长。因此采用空间换取时间的办法,在HotSpot虚拟机实现是通过使用OopMap的数据结构进行记录,在类加载完成的时候,HotSpot就把对象内什么偏移量上是什么类型的数据计算出来,也会在特定的位置记录下栈和寄存器中哪些位置是引用。这样在GC扫描时就可以直接获取这些信息了。

  解决标记阶段的问题二:在OopMap的协助下,HotSpot可以快速且准确的完成GCRoot的枚举,但是如果在每一条指令都生成OopMap,那将消耗极大的空间,实际上HotSpot也没有为每条指令生成OopMap,只是在特定的位置记录了这些信息,这些位置称为安全点,安全点的。选定以“是否具有让程序长时间执行的特征”为标准进行判定的。也就是在方法调用 循环跳转 异常跳转等,所以具有这些功能的指令才会产生SavePoint.

对于安全点又需要考虑的一个问题,选定好后怎么让所有线程都停止在这个安全点上,这里有两种方案可供选择一种是抢先式中断,另外一种则是主动式中断。

抢先式中断是在GC停顿时所有线程停止,在将没有到安全点的线程恢复,到了安全点再停止。

主动式中断则不直接对线程进行操作,仅仅简单的设定一个标志,各个线程执行时查询这个标志,当标志为true时线程达到安全点就将直接挂起。

使用SavePoint(安全点)似乎完美的解决GC进入安全点的问题,但是如果碰到了线程“不执行”的情况(sleep),JVM不可能等到线程休眠后再到安全点,于是就需要安全区域(SafeRegion)来解决。安全区域是指在一段代码中,引用关系不会发生变化,。

在线程执行到SafeRegion时,首先标志自己已经进入Safe Region区域.这段时间JVM如果发起GC就不需要管标识为Safe Region的线程了。在线程离开Safe Region时,需要检查系统是否完成了根节点枚举,如果完成,那线程继续执行,否则就必须等待可以安全离开Safe Region为止。

以上就是具体的垃圾回收(GC)的阶段过程以及HotSpot的算法实现。

猜你喜欢

转载自blog.csdn.net/weixin_40234548/article/details/81428089
今日推荐