0
点赞
收藏
分享

微信扫一扫

垃圾回收算法(第一部分)

耳一文 2022-02-15 阅读 101

目录

  • 标记阶段:引用计数算法、可达性分析算法
  • 对象的finalization机制
  • MAT与Jprofiler的GC Roots溯源
  • 清除阶段:标记-清除算法
  • 清除阶段:复制算法
  • 清除阶段:标记-压缩算法
  • 分代收集算法
  • 增量收集算法、分区算法

1、标记阶段:引用计数算法、可达性分析算法

垃圾标记阶段:对象存活判断

  • 在堆里存放着几乎所有的Java对象实例,在GC执行垃圾回收之前,首先需要区分出内存中哪些是存活对象,哪些是已经死亡的对象,只有被标记位已经死亡的对象,GC才会在执行垃圾回收时,释放掉其所占用的内存空间,因此这个过程我们可以成为垃圾标记阶段
  • 那么在JVM中究竟是如何标记一个死亡对象呢?简单来说,当一个对象已经不再被任何的存活对象继续引用时,就可以宣判为已经死亡。
  • 判断一个对象存活一般有两种方式:引用计数算法可达性分析算法

方式1:引用计数算法

  • 引用计数算法比较简单(Reference Counting),对每个对象保存一个整型的引用计数器属性。用于记录对象被引用的情况。

  • 对于一个对象A,只要有任何一个对象引用了A,则A的引用计数器就加1,;当引用失效时,引用计数器就减1.只要对象A的引用计数器的值为0,即表示对象A比可能再被使用,可进行回收。

  • 优点:

    1.实现简单,垃圾对象便于辨识
    2.判定效率高,回收没有延迟性

  • 缺点:
    1.他需要单独的字段存储计数器,这样的做法增加了存储空间的开销
    2.每次赋值都需要更新计数器,伴随着加法和减法操作,这增加了时间开销
    3.引用计数器有一个严重的问题,即无法处理循环引用的情况。这是一条致命缺陷。导致在Java的垃圾收集器中没有使用这类算法

引用计数算法小结:

  • 引用计数器算法,是很多语言的资源回收选择,例如因人工智能而更加火热的Python,他更是同时支持引用计数和垃圾收集机制。
  • 具体哪种最优是要看场景的,业界有大规模实践中保留引用计数器机制,以体改吞吐量的尝试。
  • Java并没有选择引用计数,是因为其存在一个基本的难题,也就是很难处理循环引用关系。
  • Python如何处理循环引用?

方式2:可达性分析算法

也叫根搜索算法、追踪性垃圾收集

  • 相对于引用计数算法而言,可达性分析算法不仅永阳具备实现简单和执行高效等特点,更重要的是该算法可以有效地解决在引用计数算法中循环引用的问题防止内存泄露的发生
  • 相较于引用计数算法,这里的可达性分析就是Java、C#选择的、这种类型的垃圾收集通常也叫追踪性垃圾收集(Tracing Garbage Collection)。
  • 所谓“GC Roots”根集合就是一组必须活跃的引用。
  • 基本思路:

在这里插入图片描述

2、对象的finalization机制

  • Java语言提供了对象终止(finalization)机制来允许开发人员提供对象被销毁之前的自定义处理逻辑
  • 当垃圾回收器发现没有引用指向一个对象,即:垃圾回收对象之前,总会先调用这个对象的finalize()方法
  • finalize()方法允许在子类中被重谢,用于在对象被回收时进行资源释放。通常在这个方法中定义一些资源释放和清理的工作,比如关闭文件、套接字和数据库连接等。
  • 永远不要主动调用某个对象的finalize()方法,应该交给垃圾回收机制调用。里有包括如下三点:
  • 从功能上来说,finalize()方法与C++中的析构函数比较像是,但是Java采用的是基于垃圾回收器的自动内存管理机制,所以finalize()在本质上不同于C++ 中的析构方法。
  • 由于finalize()方法的存在,虚拟机中的对象一般处于三种可能的状态。
  • 如果从所有的根结点都无法访问到某个对象,说明对象已经不再使用了。一般来说,此对象需要被回收。但事实上,也并非是“非死不可”的,这时候它们暂时处于“缓刑”阶段。一个无法触及的对象有可能在某一个条件下“复活自己”,如果这样,那么对它的回收就是不合理的,为此,定义虚拟机中的对象可能的三种状态。如下:
  • 以上三种状态中,是由于finalize()方法的存在,进行的区分。只有在对象不可触及时才可以被回收。

具体过程

判断一个对象objA是否可回收,至少要经历两次标记过程:
1.如果对象objA到GC Root没有引用链,则进行第一次标记。
2.进行筛选,判断此对象是否有必要执行finalize()方法
a.如果对象objA没有重写finalize()方法,或者finalize ()方法已经被虚拟机调用过,则虚拟机视为“没有必要执行”,objA被判定为不可触及的。
b.如果对象objA重写了finalize()方法,且还未执行过,那么objA会被插入到F-Queue队列中,由一个虚拟机自动创建的、低优先级的Finalizer线程触发其finalize()方法执行。
c.finalize()方法是对象逃脱死亡的最后机会,稍后Gc会对r-Queue队列中的对象进行第二次标记。如果objA在finalize()方法中与引用链上的任何一个对象建立了联系,
那么在第二次标记时,objA会被移出“即将回收”集合。之后,对象会再次出现没有引用存在的情况。在这个情况下,finalize方法不会被再次调用,对象会直接变成不可触及的状态,也就是说,一个对象的finalize方法只会被调用一次。

举报

相关推荐

0 条评论