Java虚拟机之垃圾收集算法

1.对象是不是已经死亡

1)引用计数法

给对象添加一个引用计数器,每当有一个地方引用它时,计数器值就加1,当引用失效时,计数器值就减1,只要对象的引用计数器的值为0,那么对象就不可能再被使用。

引用计数的缺点:很难解决对象之间相互循环引用的问题,如下图,objA指向对象A,objB指向对象B,现在将objA.instace = objB,同时将objB.instance = objA,然后将objA=null,objB=null,此时这两个对象已经不能被访问了,但是因为它们互相引用着导致引用计数都不为0,于是引用计数算法无法通知GC收集器回收它们。(其实这时也是一种内存泄漏)


2)可达性分析算法

从称为"GC Roots"的对象开始搜索,如果从GC Roots到某个对象不可达,则证明此对象是不可用的,Java中采用的是这种方法。

2.常用的垃圾收集算法

1)标记—清除算法

标记—清除算法将垃圾回收分为两个阶段:标记阶段和清除阶段,标记阶段使用的可达性分析算法标记未被引用的垃圾对象,如下图所示,没有被箭头指向的就是垃圾对象。然后,在清除阶段,清除所有的未被标记的对象。



缺点:上面右图,使用标记—整理算法会产生大量不连续的内存碎片,内存碎片太多,导致在分配大对象的时候找不到连续的内存而不得不提前触发一次GC。

2)标记—整理算法

标记—整理算法也分为两步,不过与标记—清除算法不同的是,标记—整理算法,在标记完成后,不是直接对可回收对象进行清理,而是让所有存活的对象都向一端移动,然后直接清理掉边界以外的区域,如下图所示。



3)复制算法

将可用内存分为大小相同的两块,每次只使用其中的一块,如下图,当左边的内存使用完了,将存活着的对象复制右边的另一块上,然后将左边的整块清理回收。


优点:清理一次后,存在对象连续的放在内存中,减少了内存碎片,同时对象较多的时候,要复制很多对象

缺点:但是代价内存缩小为原先的一一半

3.Java采用的垃圾收集算法

1)将内存空间分为新生代和老年代,新生代采用复制算法,老年代采用标记—整理算法。同时新生代又细分为一块较大的eden区和两块较小的Survivor空间,每次使用eden区和一块Survivor。

2)当回收时(触发一次Minor GC),将eden和一块Survivor中存活的对象移到另一块Survivor,可能奇怪另一块Surivivor怎么装得下呢?因为新生的对象朝生夕死,回收时大多数对象都被清除了,剩下的放在另一块Survivor是放得下的,过程如下图的黄线和红线所示。

3)对于长期存活的老对象(年龄大于15岁,也就是说经历过15次Minor GC),将被移动到老年代,因为如果15次Minor GC还没有被回收,说明该对象比较问题,大概率还会长期存在,此时任然在两个Survivor中反复复制是对资源的浪费。如下图最左边的绿色箭头所示。

4)对于大对象(字符串和数组)直接进入老年代,原因一是Survivor区不一定能够装下,即使Survivor能够装下,也会占很大量Survivor的空间,导致小对象无处可放提前触发Minor GC。





4.回收后内存空间如下图



参考:https://www.bilibili.com/video/av5820102/index_4.html?t=2589

猜你喜欢

转载自blog.csdn.net/chenkaibsw/article/details/80045575