JVM之垃圾回收算法(1)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/MrLiar17/article/details/84585322

1、JVM垃圾回收算法

标记-清除算法:标记清除分为两个过程,即标记阶段和清除阶段。首先从根Root出发可以达到的对象进行标记,遍历堆进行未标记删除。缺点:①标记清除效率不高,需要遍历整个堆空间,②会产生碎片化空间,清除后产生不连续的地址空间,当程序在以后的运行过程中需要分配较大对象时无法找到足够的连续内存而不得不提前触发另一次垃圾收集动作。清除时会暂停程序的进行,等待标记清除结束后才会恢复应用程序的运行,这也是Stop-The-World这个单词的来历。该算法一般应用于老年代,因为老年代的对象生命周期比较长。

判断对象是否存活一般有两种方式:

引用计数:每个对象有一个引用计数属性,新增一个引用时计数加1,引用释放时计数减1,计数为0时可以回收。此方法简单,无法解决对象相互循环引用的问题。

可达性分析(Reachability Analysis):从GC Roots开始向下搜索,搜索所走过的路径称为引用链。当一个对象到GC Roots没有任何引用链相连时,则证明此对象是不可用的。不可达对象。

复制算法:将可用内存按容量划分为大小相等的两块,每次只使用一块,当这块内存用完后,会将还存活的对象复制到另一块等大的内存中,最后再把已使用过的内存空间清理掉。这样每次都只会对半个内存回收,分配时不需要考虑内存空间碎片等问题。缺点:但这样的代价就是牺牲了一半的内存,成本太高。

这种算法适用于处理新生代 ,在Java JVM中新生代不需要按照1:1的比例来分配,而是分为一块较大的Eden(伊甸园区) 和 两块Survivor(存活区S0,S1)区域(S0,S1一个是from,另一个是to,但他们不是固定的,他们会随着Minor GC发生变化),每次使用Eden和其中一块的Survivor区域,当进行垃圾回收时,会将Eden和使用过的Survivor中存活的对象复制到另一块没使用过的Survivor区域中。HotSpot默认Eden 和 两块Survivor比例为8:1:1,这样相对1:1浪费一半的内存来说,jvm只浪费了10%的堆内存。

为什么要设置两个Survivor区?

一旦Eden满了,触发一次Minor GC,Eden中的存活对象就会被移动到Survivor区。这样继续循环下去,下一次Eden满了的时候,问题来了,此时进行Minor GC,Eden和Survivor各有一些存活对象,如果此时把Eden区的存活对象硬放到Survivor区,很明显这两部分对象所占有的内存是不连续的,也就导致了内存碎片化,所以会存在两个Surivor区。

年轻带到老年代的过程?

在Eden中,经历一次Minor GC,Eden中的存活对象就会被移动到第一块survivor space S0,Eden被清空;等Eden区再满了,就再触发一次Minor GC,Eden和S0中的存活对象又会被复制送入第二块survivor space S1(这个过程非常重要,因为这种复制算法保证了S1中来自S0和Eden两部分的存活对象占用连续的内存空间,避免了碎片化的发生)。S0和Eden被清空,然后下一轮S0与S1交换角色,如此循环往复。如果对象的复制次数达到16次,该对象就会被送到老年代(Old Gen)中。

上述机制最大的好处就是,整个过程中永远有一个survivor space是空的,另一个非空的survivor space无碎片。

扫描二维码关注公众号,回复: 4930708 查看本文章

标记-整理算法:标记过程与“标记-清除”算法一样,但后续步骤不是直接对可回收对象进行清理,而是让所一端移动,然后直接清理掉端边界以外的内存。

分代收集算法:一般是把Java堆分为新生代和老年代,根据各个年代的特点采用最适当的收集算法。新生代都发现有大批对象死去,选用复制算法。老年代中因为对象存活率高,必须使用“标记-清理”或“标记-整理”算法来进行回收。

猜你喜欢

转载自blog.csdn.net/MrLiar17/article/details/84585322