JVM垃圾回收(一)

垃圾回收

1、什么是垃圾回收

程序运行必然需要申请资源,无效的对象资源如果不及时处理就会一直占用内存资源,最终导致内存溢出,所以对内存资源的管理是非常重要的

2、C/C++语言的垃圾回收

    在C/C++语言中,没有垃圾回收机制,是通过new关键字申请内存资源,通过delete关键字释放内存资源。如果程序员在某些位置没有写
delete进行释放,那么申请对象将一直占用内存资源,最终导致内存溢出。

3、Java语言的垃圾回收

    为了让程序员更专注于代码的实现,而不过多关心内存释放的问题,所以,在Java语言中,有了自动的垃圾回收机制,也就是我们熟悉的
GC了。有了垃圾回收机制,程序员在只需要关心内存的申请即可,内存的释放有系统自定识别。换句话说,自动的垃圾回收算法就会变得非常
重要了,如果因为算法的不合理,导致内存资源一直没有释放,同样也可能导致内存的溢出。
	当然,除了Java语言,C#、Python的语言也都有自动的垃圾回收机制。

4、垃圾回收的常见算法

自动化的管理内存资源,垃圾回收机制必须有一套算法进行计算,哪些是有效的对象,哪些是无效的对象,对于无效的对象,就要进行回收处理

常见的垃圾回收算法有:引用计数法、标记清除法、标记压缩法、复制算法、分代算法等。

4.1 、引用计数法

引用计数法是历史悠久的一种算法,最早George E.Collins在1960年的时候,首次提出,50年后的今天,该算法依然被很多编程语言使用。
4.1.1 、原理

假设一个对象A,任何一个对象对A的引用,对象A的引用计数器就会+1,当引用失败时,对象A的引用计数器就会-1,如果对象A的引用计数器的值为0,就说明对象A没有引用了,就可以回收。

4.1.2、优缺点
  • 优点
    • 实时性较高,无需等到内存不够的时候,才开始回收,运行时根据对象的计数器是否为0,就可以直接回收。
    • 在垃圾回收过程汇总,应用无需挂起。如果申请内存时,内存不足,则立刻报OutOfMember错误。
    • 区域性,更新对象的计数器时,只是影响到该对象,不会扫描全部对象。
  • 缺点
    • 每次对象被引用时,都需要更新计数器,有一点时间开销;
    • 浪费CPU资源,即使内存够用,仍然在运行,进行计数器的统计;
    • 无法解决循环引用的问题(最大的缺点)。

什么是循环引用?

Public Class TestA {
	private TestB b;
}
Public Class TestB {
	private TestA a;
}
public class Main{
	public static void main(String[] args) {	
		TestA a = new TestA();
		TestB b = new TestB();
		a.b = b;
		b.a = a;
		a = null;
		b = null;
	}
}
   从上述代码来看,a、b对象均为null,是可以被回收的,但是,a对象引用了b对象,a的计数器为1,b对象引用了a对象,
b的计数器也为1,计数器不为0,算法无法回收a、b对象,这就是引用计数器的最大的缺点。

4.2、标记清除法

标记清除法,是将垃圾回收分为2个阶段,分别是标记和清除。
  • 标记:从根节点开始标记引用的对象
  • 清除:未被标记引用的对象就是垃圾对象,可以被清理
4.2.1 原理

在这里插入图片描述
这种图代表的是程序运行期间所有对象的状态,他们的标志位全部是0(也就是未标记,以下默认0就是未标记,1就是已标记),假设这会儿有效内存空间耗尽了,JVM将会停止应用程序的运行并开启GC线程,然后开始进行标记工作,按照跟搜索算法,标记完以后,对象的状态如下图:
在这里插入图片描述
可以看到,按照根搜索算法,所有从root对象可达的对象就被标记为存活的对象,此时,已经完成了第一阶段标记。接下来就要执行第二阶段清楚了,那么清楚完以后,剩下的对象以及对象的状态如下图所示。在这里插入图片描述
可以看到,没有被标记的对象将会回收清除掉,而被标记的对象将会留下,并且会将标记位重新归0,接下来就是唤醒停止的程序线程,让程序继续运行即可。

4.2.2、优缺点

可以看到,标记清除法解决了引用计数算法中循环引用的问题,没有从root节点引用的对象都会被回收。同样,标记清除法也有缺点。

  • 效率较低,标记和清除两个动作都需要遍历所有对象,并且在GC时,需要停止应用程序,对于交互要求比较高的应用而言,这个体验非常差的。
  • 通过标记轻蹙算法清理出来的内存,碎片化较为严重,因为被回收的对象可能存在于内存的各个角落,所以清理出来的内存是不连贯的。

4.3、标记压缩法

标记压缩算法是在标记清除算法的基础上,做了优化改进的算法。和标记清除算法一样,也是从根节点开始,对对象的引用进行标记,在清理阶段,并不是简单的清理未标记的对象,而是将存活的对象压缩到内存的一段,然后清理边界意外的对象,从而解决碎片化的问题。
4.3.1、原理

在这里插入图片描述

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

4.4、复制算法

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

如果内存中垃圾对象较多,需要复制的对象就较少,这种情况下适合用该方式并且效率比较高,反之,则不适合。
在这里插入图片描述

4.4.1、JVM中年轻代内存空间

在这里插入图片描述

  1. 在GC开始的时候,对象只会存在于Eden区和名为“From”的Survivor区,Survivor区“To”是空的。
  2. 紧接着进行GC,Eden区中所有存活的对象都会被复制到“To”,而“From”区中,仍存活的对象会根据他们的年龄决定去向。年龄到达一定值(年龄阈值,可以通过-XX:MaxTenuringThreshold来设置)的对象会被移动到年老代中,没有达到阈值的对象会被复制到“To”区域。
  3. 经过这次GC后,Eden区和From区已经被清空。这个时候,“From”和“To”会交换他们的角色,也就是新的“To”就是上次GC前的“From”,新的“From”就是上次GC前的“To”。不管怎样,都会保证名为To的Survivor区域是空的。
  4. GC会一直重复这样的过程,直到“To”区域被填满,“To”区被填满之后,会将所有的对象移动到年老代中。
4.4.2、优缺点
  • 优点
    • 在垃圾对象多的情况下,效率较高
    • 清理后,内存无碎片
  • 缺点
    • 在垃圾对象少的情况下,不适用,如:老年代内存
    • 分配的2块内存空间,在同一时刻,只能使用一半,内存使用效率低下

4.5、分代算法

分代算法其实就是,根据回收对象的特点进行选择,在jvm中,年轻代适合使用复制算法,老年代适合使用标记清除法或标记压缩算法。
  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值