浅谈JVM垃圾回收算法

1 垃圾回收

        什么是垃圾回收,程序运行的时候必须申请内存资源,无效的对象资源会一直占用内存,如果不及时处理就会堆积在内存中,在程序运行到一定时间后造成内存溢出。

        在c/c++中没有有垃圾回收机制,通过new关键字申请的内存资源,需要我们自己通过delete关键字来释放内存资源,如果在编程过程中我们没有使用delete来释放,那么申请对象就会一直存在在内存中,最终导致内存溢出。

        在Java中为了让程序员更加专注于代码实现,不用考虑内存回收的问题,Java有自己的自动回收机制,也就是我们常说的GC。GC可以代替我们进行内存释放,让程序员只关系内存申请。

        因此GC的算法就很重要,如果算法运用不合理,导致资源得不到及时释放,这样可能就会有内存溢出的风险,也就是我们常说的OOM

除了Java以外,C#和Python也有自己的垃圾回收机制

2 常见的垃圾回收算法

GC需要自动回收内存对象,就需要有好的算法支撑,用来选着那些需要被回收,那些需要保留

常见的算法有引用计算法,标记清除法,标记压缩法,复制算法,分代算法等

2.1 引用计算法

引用计算是最早的一种算法,最早George E. Collins在1960的时候首次提出,到目前为止该算法也在很多编程语言中使用

2.1.1 原理

假设有个对象A,任何一个对象对象A引用,就在对象A的基数器上+1,当引用失效的计数器就-1,如果对象A的计数器为0,表名改对象没有被引用过,表示可以被回收了

2.1.2 优缺点

优点:

  • 实时性高,无需等待内存不足的时候才开始回收,运行时根据计数器是否为0,就可以执行回收

  • 在回收过程中应用不会挂起。在申请内存的时候,内存不足则立刻报OOM

  • 区域性,更新对象计数器只会影响到该对象,不会扫描全部对象

缺点:

  • 每次对象被引用都需要去更新计数器,存在一部分开销

  • 浪费CPU资源,即使内存足够,仍然在运行的时候进行计数器统计

  • 无法解决循环引用的问题(A->B->A)

2.1.3循环引用

public class A{
    public B b;
}
public class B{
    public A a;
}
public class Main{
  public static void main(String[] args){
      A a = new A();
      B b = new B();
      a.b=b;
      b.a=a;
      a = null;
      b = null;
  }

虽然对象a和对象b都为null,应该被护手,但是此时a的计算器为1,b的计数器也为1,这样a,b都不会被回收掉

2.2 标记清除法

标记清除法将回收分为两个阶段,分别为标记和清除

标记:从根节点开始标记引用对象

清除:未被标记的对象就是垃圾对象,可以被清理

2.2.1 原理

在内存资源未耗尽之前,所有对象的mark都未0,(0表示未标记,1表示标记)。当内存不够的需要启动GC的时候,JVM将停止应用程序所有的线程,启动GC线程,然后开始标记工作,按照根搜索算法标记对象,结果如上图所示。

从图上我们可以看到,所有从root对象可达的对象都是存活对象,那么第一阶段的标记工作就完成了,这时候需要执行第二阶段的清除工作,清除掉上图标记为0的对象结果如下所示

这时我们发现回收调了标记为0的对象同时所有对象的标记都被置为了0,接下来就是唤醒应用程序的线程,让程序继续运行

整体过程如下

 

 2.2.2 优缺点

可以看到,标记和清除算法解决了玄幻引用的问题,没有被root根对象引用的都会被回收,同样标记算法的缺点也很明显

效率低,标记和清除都需要遍历所有的对象,并且在GC的时候回挂起所有的应用程序线程,这对交互性很高的应用而言体验很很差

通过标记清除后磁盘碎片很多(因为你回收对象的时候相当于死随机回收,对应磁盘的空间也是变成了随机分布),清理出来的空间不是连贯的

2.3 标记压缩算法

标记压缩算法就是在标记清除算法的基础上改进了清除操作,在清除的时候并不是简单的清除对象,而是将存活的对象压缩到内存的一端,然后清理边界以外的垃圾,从而解决碎片化的问题

2.3.1 原理

2.3.2 优缺点

该算法解决了磁盘的碎片话,但是未能解决STW以及需要两次遍历所有对象,并且多了移动压缩这一步,效率上要更低一些

2.4 复制算法

复制算法的核心就是将内存空间一分为2,每次使用只有其中一块内存,在垃圾回收的时候讲存活的对象复制到另外一块内存中,然后将原来的内存空间清空,交换两个内存的角色,完成垃圾回收。

2.4.1 JVM中年轻代空间

1 在GC 开始的时候所有对象只会存在于Eden和名为from的survivor区,survivor中的to区是空的

2 在GC开始的时候。Eden中的存活对象会被复制到to区中,同事from区中还存活的对象会根据年龄值确定其 去向如果年龄达到一定的阈值(-XX:MaxTenuringThreshold 设置阈值)就会被复制到老年代中,没有达到阈值就会被复制到to区

3 这时候Eden区以及from区中的对象被情况,from和to交换他们角色,也是就说新的to就是刚才被清空的from区,不管如何在年轻代空间中总会有一个to区是被空闲的

4 GC会一直重复这个过程,直到to区被填满,填满后对象会被移动到老年代中

2.4.2 优缺点

有点:

  • 在垃圾清理对象多的情况下,效率高

  • 清理后内存无锁片

缺点

  • 垃圾对象少的情况下不适用(因为对象少的话代表每次能清理出来的空间有限,刚清理了内存一会又满了又清理),比如老年代内存

  • 分配了两块空间只有一块被使用,内存使用率低

2.5 分代算法

其实就是在年轻代和老年代中使用不同算法,年轻代适用于复制算法,老年代适用于标记压缩算法或者标记清除算法

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值