- 引用计数算法
- 可达性算法
2.1 标记清除算法
2.2 复制算法
2.3 标记整理算法
一、 引用计数器算法
算法实现简单,效率很高。但是该算法被抛弃
,不会用在jvm中清除对象,因为不能解决相互引用的问题。(可以看完下面的然后回过头来看这段话)
算法基本思想:
给每个对象增加一个引用计数器
,有一个引用就+1,一个引用失效时就-1,当引用计数器数字为0时候,这个对象就不可能在被使用,就会被回收。
如果使用这种算法,那么相互引用的情况下无法被GC回收的案例如下:
import org.junit.Test;
public class GcTest {
class A{
private B b;
public void setB(B b) {
this.b = b;
}
}
class B{
private A a;
public void setA(A a) {
this.a = a;
}
}
@Test
public void test(){
A a = new A();
B b = new B();
a.setB(b);
b.setA(a);
//========
a = null;
b = null;
}
}
显然当a、b置为null时,jvm会GC掉这个对象的。
二、可达性算法
关键字:
- 可达性分析 Reachability Analysis
- 引用链 Reference Chain
算法基本思想:
引用链是个树形结构,以GC Roots为起点向下搜索,所走过的路径就是引用链,当发现一个对象和GC Roots间没有任何引用链,那么这个对象就是不可用的,应该被GC。
如图2.1所示,绿色的对象到GC Roots间没有任何引用链,所以都是不可达的,都是不可用对象。
![](https://img.haomeiwen.com/i1055570/1333b2e1f216f8ab.png)
基于可达性算法,如下是GC的回收对象具体实现的理论算法。
2.1 标记清除算法
比较基础的算法了,见名知意,就是把不可达对象标记起来,然后统一交给GC去回收该对象内存。
问题:
会造成大量不连续的内存碎片,如果碎片太多的话,如果来个较大的对象,发现无法分配内存就不得不提前触发GC回收内存。
见图知意,如图2.2所示,会收回会发现有很多不连续的内存。
![](https://img.haomeiwen.com/i1055570/1d05de5f646b056b.png)
2.2 复制算法
把内存空间对半分,先紧着一半的内存A用,用完了就触发GC把活着的对象复制到另一半B并清空A,这时候AB内存使用对调。
好处:
分配对象的时候不用考虑碎片等复杂问题,只要移动堆顶指针按顺序分配即可,实现简单,运行高效。
坏处:
- 将内存分配减小到了一半,代价比较高。
- 存活率较高的对象时要进行较多的复制,效率变低。
- 不想浪费50%空间就需要额外的空间进行
分配担保
以应对被使用的对象100%存活的极端情况。所以老年代一般不直接选用这种算法
。
变化:spot将内存分为Eden、from suvivor和to suvivor 8:1:1
来采用复制算法。(用来处理年轻代对象回收);分配担保机制;
继续看图说话,如图2.3
![](https://img.haomeiwen.com/i1055570/56a0a406b255ec0d.png)
2.3 标记-整理算法
老年代特点,解决复制算法的问题。
仍然是标记-清楚算法,但是后续步骤还会让存活对象向一端移动,然后清除掉端边界以外
的内存。
![](https://img.haomeiwen.com/i1055570/f003367e5b15e00b.png)
2.4 分代回收算法
spot虚拟机根据存货周期将内存分为年轻代、老年代。
年轻代分为Eden、2个suvivor区。
主要还是采用复制算法。
网友评论