常见的垃圾回收算法

​ 今天我总结一下一些常见的垃圾回收算法的优缺点和使用场景相关的问题,这里我是参考了《深入理解jvm虚拟机》以及一些网上的文章进行的总结。

1.垃圾回收的概述

​ 我们都知道jvm是具有垃圾回收机制的,与c/c++不同,java程序员不需要在写程序的时候考虑垃圾回收的问题,只需要专注代码逻辑即可,一定程度上减轻了程序员的负担。在jvm中,垃圾回收主要发生的地方是在堆内存中,因为在jvm的栈内存中,每个栈帧所需的内存大小是在编译期间就已经确定好了的,因此这个区域的内存分配和回收都是具有确定性的,也就不需要考虑过多的回收的问题,方法结束后,栈帧也就出栈了,内存也就跟着回收了。

​ 而在堆内存中,我们只有在程序运行期间才会知道创建了哪些对象,这部分内存的分配和回收都是动态的,所以需要垃圾收集器关注这一块内存区域。

2.引用计数法

​ 引用计数法是一种历史悠久的算法,最早是在1960年George E. Collins首次提出的,该算法的实现是假设有一个对象A,任何一个对象对A的引用,那么对象A的引用计数器+1,当引用失败时,对象A的引用计数器就-1,如果对象A的计数器的值为0,就说明对象A没有引用了,可以被回收。但是在目前主流的java虚拟机中并没有使用引用计数法,其中主要原因也是因为引用计数法无法回收循环引用的变量。

例如

public class LexGC {
	public static void main(String[] args) {
		TestA a = new TestA();
		TestB b = new TestB();
		a.b=b;
		b.a=a;
		a = null;
		b = null;

	}
}
class TestA{
	public TestB b;
}
class TestB{
	public TestA a;
}

虽然a和b都为null,但是由于a和b存在循环引用,这样a和b永远都不会被回收。

优点:

1.实时性较高,无需等到内存不够的时候,才开始回收,运行时根据对象的计数器是否为0,就可以直接回收

2.在垃圾回收过程中,应用无需挂起。如果申请内存时,内存不足,则立刻报outofmember 错误

3.区域性,更新对象的计数器时,只是影响到该对象,不会扫描全部对象

缺点:

1.每次对象被引用时,都需要去更新计数器,有一点时间开销

2.浪费CPU资源,即使内存够用,仍然在运行时进行计数器的统计

3.无法解决循环引用问题

3.标记清除算法

​ 在介绍标记清除算法之前,这里要先提一下可达性分析算法,所谓可达性分析就是用来判断对象是否存活,这个算法的基本思路就是以“GC Roots”(在java中,虚拟机栈中的引用对象,方法区中静态属性引用对象,方法区中常量引用对象,本地方法栈中的JNI都可以最为GC Roots)为起始点,从这个节点开始向下搜索,搜索所走过的路径称为引用链,当一个对象到GC Roots没有任何引用链时,也就是说从GC Roots到这个对象是不可达的,则这个对象就会被判定为可回收对象。

​ 现在在来说一下标记清除算法,该算法是将垃圾的回收分为两个阶段,分别为标记和清除,首先是标记,如下图,会采用可达性分析算法,找出可用和不可用的对象,将可用的对象的mark值设置为1,不可用的为0。然后就会进行第二个阶段,也就是清除阶段,这里会把mark为0的对象进行垃圾回收,然后将剩余对象的mark设为0,等待下一次标记。

在这里插入图片描述

优点:由图中也可以看到该算法解决了引用计数算法中循环引用对象的回收问题

缺点:

1.效率较低,在标记和清除阶段都需要遍历所有的对象,而且在GC的时候会短暂的停止应用程序,用户体验较差

2.通过标记清除算法清理出来的内存,碎片化较为严重,因为被回收的对象可能存在于内存的各个角落,所以清理出来的内存是不连贯的

4.标记压缩算法

​ 标记压缩算法是在标记清除算法上做了改进和优化,标记压缩算法是在标记清除算法的基础之上,做了优化改进的算法。和标记清除算法一样,也是从根节点开始,对对象的引用进行标记,在清理阶段,并不是简单的清理未标记的对象,而是将存活的对象压缩到内存的一端,然后清理边界以外的垃圾,从而解决了碎片化的问题。
在这里插入图片描述

优缺点:优缺点同标记清除算法,解决了标记清除算法的碎片化的问题,同时,标记压缩算法多了一步,对象移动内存位置的步骤,其效率也有有一定的影响。

5.复制算法

​ 复制算法就是将没存空间一分为二,存储时只使用其中的一块空间,当进行垃圾回收的时候,找出正在使用的对象,并将这些对象复制到另一块内存空间中,然后将该内存清空,交换两个空间的角色,实现垃圾的回收。

​ 在jvm新生代中,Survivor区就是采用复制算法实现的垃圾回收。

  1. 在GC开始的时候,对象只会存在于Eden区和名为“From”的Survivor区,Survivor区“To”是空的。
  2. 紧接着进行GC,Eden区中所有存活的对象都会被复制到“To”,而在“From”区中,仍存活的对象会根据他们的年龄值来决定去向。年龄达到一定值(年龄阈值,可以通过-XX:MaxTenuringThreshold来设置)的对象会根据他们的年龄值来决定去向。年龄达到一定值(年龄阈值,可以通过-XX:MaxTenuringThreshold来设置)的对象会被移动到年老代中,没有达到阈值的对象会被复制到“To”区域。
  3. 经过这次GC后,Eden区和From区已经被清空。这个时候,“From”和“To”会交换他们的角色,也就是新的“To”就是上次GC前的“From”,新的“From”就是上次GC前的“To”。不管怎样,都会保证名为To的Survivor区域是空的。
  4. GC会一直重复这样的过程,直到“To”区被填满,“To”区被填满之后,会将所有对象移动到年老代中

优点:

1.如果垃圾对象较多的情况下,该算法效率比较高

2.垃圾清理之后,内存不会出现碎片化

缺点:

1.并不适用在垃圾较少的情况下适用,例如老年代中

2.分配的2块内存空间,在同一个时刻,只能使用一半,内存使用率较低

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值