GC的算法与种类

GC的算法与种类

引用计数法

引用计数器的实现很简单,对于一个对象A,只要有任何一个对象引用了A,则A的引用计数器就加1,当引用失效时,引用计数器就减1。只要对象A的引用计数器的值为0,则对象A就不可能再被使用。
这里写图片描述
【注】名词解释
可达对象:通过根对象进行引用搜索,最终可以到达的对象
不可达对象:通过根对象进行引用搜索,最终没有被引用到的对象

引用计数法的问题

引用和去引用伴随加法和减法,影响性能
很难处理循环引用
这里写图片描述
如图循环引用因为引用计数不为0,所以永远不会被GC。

标记-清除

标记-清除算法是现代垃圾回收算法的思想基础。标记-清除算法将垃圾回收分为两个阶段:标记阶段清除阶段。一种可行的实现是,在标记阶段,首先通过根节点,标记所有从根节点开始的可达对象。因此,未被标记的对象就是未被引用的垃圾对象。然后,在清除阶段,清除所有未被标记的对象。
这里写图片描述
标记清除存在一个问题,多次标记清楚过后容易形成过多的内存碎片

标记-压缩

标记-压缩算法适合用于存活对象较多的场合,如老年代。它在标记-清除算法的基础上做了一些优化。和标记-清除算法一样,标记-压缩算法也首先需要从根节点开始,对所有可达对象做一次标记。但之后,它并不简单的清理未标记的对象,而是将所有的存活对象压缩到内存的一端。之后,清理边界外所有的空间。
这里写图片描述
这种方式避免的内存碎片的产生

复制算法

复制算法将原有的内存空间分为两块,每次只使用其中一块,在垃圾回收时,将正在使用的内存中的存活对象复制到未使用的内存块中,之后,清除正在使用的内存块中的所有对象,交换两个内存的角色,完成垃圾回收。
这里写图片描述
与标记-清除算法相比,复制算法是一种相对高效的回收方法,但是复制算法需要多一倍的内存。

复制算法比较适用于新生代。因为在新生代,垃圾队形通常会多于存货对象,复制算法的效果会比价好。不适用于存活对象较多的场合如老年代。why?思考下

分代思想

对于复制、标记清除、标记压缩等GC算法各自都有优势和缺陷,在所有的算法中没有一种算法能够替代其他算法。因此根据垃圾回收对象的特性,使用合适的算法回收,才是明智的选择。

分代算法就是这种思想,它将内存区间根据对象的特点分成几块,根据每块内存区间的特点,使用不同的回收算法,以提高垃圾回收的效率。

通常情况下JVM虚拟机的堆分为新生代和老年代。新生代的特点是对象存活时间一般不长,大约90%的新建对象会被很快回收,因此,新生代比较适合使用复制算法。当一个对象经过几次回收之后依然存活,对象就会被放入老年代的内存空间。在老年代中,几乎所有对象都是经过几次GC之后依然得以存活的。因此,可以任务这些对象,在一定时期内,甚至在应用程序的整个生命周期中,将是常驻内存的。所以使用标记清除或者标记压缩算法比较合适。

分区思想

分带算法按照对象的生命周期长短划分为两个部分,分区算法将整个堆空间划分成连续的不同小区间,每个小区间独立使用独立回收。这种算法的好处是可以控制一次回收多少个区间。这样可以让GC导致停顿时间拆短,如此GC多次每次时间变短。

谁才是真正的垃圾——判断可触及性

所有的算法,需要能够识别一个垃圾对象,因此需要给出一个可触及性的定义:即从根节点开始是否可以访问到这个对象,如果可以,则说明当前对象正在被使用,如果从所有的根节点都无法访问到某个对象,说明对象已经不再使用了,一般来说,此对象需要被回收。

简单来说可触及性包括以下3种状态:
可触及的:从根节点可以开始,可以到达这个对象。
可复活的:对象的所有引用被释放,不过finalize()函数没有被调用,可能在finalize()函数中复活。
不可触及的:对象的finalize()函数被调用,并且没有复活。
以上三种状态只有对象在不可触及时才可以被回收。

【注:根节点】
栈中引用的对象
方法区中静态成员或者常量引用的对象(全局对象)
JNI方法栈中引用对象

【注:finalize】
- finalize()是Object的protected方法,子类可以覆盖该方法以实现资源清理工作,GC在回收对象之前调用该方法。
- finalize()与C++中的析构函数不是对应的。C++中的析构函数调用的时机是确定的(对象离开作用域或delete掉),但Java中的finalize的调用具有不确定性
- 不建议用finalize方法完成“非内存资源”的清理工作,但建议用于:① 清理本地对象(通过JNI创建的对象);②作为确保某些非内存资源(如Socket、文件等)释放的一个补充:在finalize方法中显式调用其他资源释放方法。其原因可见下文[finalize的问题]
但是finalize()依然是一个糟糕的模式,不推荐使用,因为finalize()可能发生引用外泄,无意中复活对象。更为重要的是finalize()是系统调用的,调用时间不明确,而try-catch-finally完全可以有更加好的效果。
参考finalize方法总结

对象的复活

举个例子:
这里写图片描述
这里写图片描述

引用的强弱

java中提供4个级别的引用:强引用、软引用、弱引用和虚引用
这里略过了….

Stop-The-World

Java中一种全局暂停的现象
全局停顿,所有Java代码停止,native代码可以执行,但不能和JVM交互
多半由于GC引起
1. Dump线程
2. 死锁检查
3. 堆Dump
危害
长时间服务停止,没有响应
遇到HA系统,可能引起主备切换,严重危害生产环境。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值