JVM-垃圾回收算法

625 阅读3分钟

一、引用计数法

1.原理

统计每一个对象被引用的次数,如果引用次数为0就释放对象。能立即回收无用内存。

2.实现

当一个对象要重新赋值引用时:

  • 把新对象引用计数+1
  • 老对象引用计数-1
  • 赋值

伪代码: image.png

3.存在的问题

  • 并发场景下,对引用计数的修改需要和对象指针的修改保证同步,往往需要加锁或者复杂的无锁算法
  • 有时会引发连锁式的回收
  • 无法有效解决循环引用

循环引用.png

注意:要先加,再减,否则如果刚好减到0的话就会被回收了。

二、复制算法

复制算法.png

1.原理

把程序运行的堆分成大小相同的两半,一半为from空间,一半为to空间。利用from空间进行分配,当空间不足以分配对象的时候,触发GC。GC会把存活的对象全部复制到to空间。复制完成以后,会把from和to互换。

2.特点

  • 1.分配采用bump the pointer,每次都把top指针向后移动即可。复制的存活对象多大,指针就移动多大。
  • 2.回收是否高效取决于存活对象的比例。存活对象越少,效率越高
  • 3.无内存碎片
  • 4.需要浪费一半内存空间
  • 5.需要停顿
  • 6.实现简单

在整理的过程 需要停顿业务线程,因为在整理对象的过程,指针会发生改变。

3.对象位置发生变化,指向的引用维护方法

1⃣️引入中间层 引入间接指针.png

虽然在复制的过程中变得简单,但是中间层的分配和回收并不容易做;而且每次访问对象属性都变成了再次访问,性能的退化也是不能接受的。

2⃣️使用forwarding指针 使用forwarding指针.png

1.A复制到to空间 2.因为A指向着C,所以C也直接复制到to空间,修改C的引用,让A指向C' 3.B复制到to空间,但是B的指针还是指向的from空间的C; 4.在第二步C复制到to空间时,让C指向新的C'地址(forwarding指针)。 5.B从C中的对象头中拿到forwarding,指向新的C'。

4.提高空间利用率

将Eden空间分配成Eden,Survivor0和Survivor1区域。这样Survivor空间的浪费就可以减少了。 配置Survivor空间大小是JVM GC调参中的重要参数。 例如 -XX:SurvivorRatio=8 代表Eden:S0:S1=8:1:1

提高空间利用率.png

from:S1+Eden to:S0 第一次:把s1+Eden一起经过回收存活的放入S1;

from:S0+Eden to:S1 第二次:把S0+Eden一起经过回收存活的放入S0;

浪费的空间就只有S0或者S1的大小。

三、标记清除法

标记清除法.png

1.原理

使用链表管理所有的空闲区域。在Mark阶段(标在对象头),将所有的存活对象识别出来,将不存活的对象所占用的内存还给链表。

2.特点

  • 1.分配和回收都要操作链表 分配要查询链表哪个位置可以放得下这个对象,回收再将内存还给链表
  • 2.有内存碎片
  • 3.总体的内存空间利用率较高
  • 4.可以用很小的代价实现并发标记和清除(在标记的过程中对象指针不会发生变化,不需要停止业务线程)