JVM之垃圾回收算法【三】

上篇文章总结了内存中哪些对象可以进行回收,那么这篇就来总结垃圾收集器如何回收垃圾。

JVM为了高效回收垃圾,有如下几种垃圾回收算法:

一、标记-清除算法

在这里插入图片描述
标记清除算法是基础的垃圾回收算法,实现简单,但是会出现严重的空间碎片问题。如上图,如果此时创建一个3M大小的对象,但是没有>=3M的连续内存空间,那么就会发生GC。

二、复制算法

在这里插入图片描述
该算法是在标记清除算法的基础上来的,首先将内存区域等分为2份,每次只使用其中一份。当其中一份内存使用完的时候,将存活的对象复制到另一份上,然后清空这一份。这样每次都是对整个半区进行内存回收,不用考虑内存碎片等复杂情况,只要移动堆顶指针按顺序分配即可,实现简单,运行高效。该算法解决了标记清除算法内存空间碎片的问题,但是内存空间使用率只有50%。

目前主流的商用虚拟机都是采用复制算法来回收新生代,新生代中的对象98%都是“朝生夕死”的,所以不需要1:1来划分内存空间,而是划分为8:1:1的Eden区和两个小的Survivor区,每次使用Eden区和其中一块Survivor区。如果另外一块Survivor区没有足够空间存放上一次Minor GC存活下的对象,这些对象会直接通过分配担保机制(就像一个信用好的人每次都能按时还钱,万一还不上了有担保人,这里老年代就相当于担保人)进入老年代。

三、标记-整理算法

在这里插入图片描述
标记整理算法的标记过程和标记清除算法一样,只是清除前先将存活对象移动到一端,然后清除垃圾对象。该算法解决了标记整理算法内存空间碎片的问题和复制算法空间利用率低的问题。但是该算法在整理过程中需要整理所有存活对象的内存地址,所以效率很低。

四、分代收集算法

分代收集算法其实是在特定情况下对上述三种算法的组合。相应的把堆这块内存区域划分为新生代和老年代,其中新生代使用复制算法,老年代使用标记-清除或标记-整理算法。

内存模型:

在这里插入图片描述
默认新生代和老年代大小比例为1:2,Eden和s0和s1比例是8:1:1,可以通过参数修改。
在这里插入图片描述

Survivor区的作用是起到一个缓冲的作用,如果没有这部分,每次Minor GC, Eden中存活对象就会移动到老年代,老年代很快就被填满。

Old区发生Full GC时会发生长时间的STW(Stop - the - world), Old区内存越大耗时越久,所以Old内存并不是越大越好。Old区有大量‘老不死对象’,因为Old区没有内存担保策略,如果采用复制算法,很浪费空间,也就很容易引发OOM,所以采用标记-整理算法。

对象分配过程:
其实内存中的对象大部分都是朝生夕死的,存活时间很短。一般创建的新对象会在Eden区分配内存空间,如果Eden区内存空间不够,JVM会进行一次Minor GC, Eden区和s0或s1清空,Eden区和s0或s1清空的那个区中存活的对象移动到s0或s1中空闲的那个区,s区移动过来的存活对象年龄+1,放不下的(由于分配担保机制)和对象年龄达到默认值15的移动到老年代。然后如果Eden还是放不下这个对象,说明是个大对象,这个对象就会被直接放到老年代,如果老年代放不下了,JVM会进行Full GC,然后如果存放不下,发生OOM, 存的下就存。
如下图:(来自网络)
在这里插入图片描述

有如下三种对象会进入老年代:

大对象
大对象指需要大量连续内存空间的对象。-XX:PretenureSizeThreshold 可以设置大对象的大小,超过这个大小就是大对象,这个参数只在Serial和ParNew两个收集器下有小。

长期存活对象
虚拟机给每个对象定义了一个对象年龄(Age)计数器(在对象头中)。正常情况下对象会不断的在 Survivor 的 From 区与 To 区之间移动,对象在 Survivor 区中每经历一次 Minor GC,年龄就增加1岁。当年龄增加到15岁时,这时候就会被转移到老年代。年龄可以通过JVM参数-XX:MaxTenuringThreshold来设置。

动态对象年龄
虚拟机并不要求对象年龄必须到15岁,才会放入老年区,如果 Survivor 空间中某个年龄以下(包括该年龄)的对象总大小大于 Survivor 空间的一半,那么年龄大于等于该年龄的对象就可以直接进去老年区,动态年龄机制在Minor GC之后触发。

发布了11 篇原创文章 · 获赞 0 · 访问量 614

猜你喜欢

转载自blog.csdn.net/fei1234456/article/details/104736912
今日推荐