上一篇:002-JVM - 什么是垃圾 juejin.cn/post/690743…
我们知道,垃圾是需要被回收的,但是怎么知道哪些是垃圾呢?
垃圾标记阶段:在堆里存放着几乎所有的 Java 对象实例, 在 GC 执行垃圾回收之前, 首先需要区分出内存中哪些是存活对象, 哪些是己经死亡的对象。只有被标记为己经死亡的对象, GC 才会在执行垃圾回收时, 释放掉其所占用的内存空间, 因此这个过程我们可以称为垃圾标记阶段。 那么在 JVM 中究竟是如何标记一个死亡对象呢? 简单来说, 当一个对象己经不再被任何的存活对象继续引用时, 就可以宣判为己经死亡。 判断对象存活一般有两种方式: 引用计数算法 和 可达性分析算法。
- 引用计数法 引用计数算法 (Reference counting) 比较简单, 对每个对象保存一个整型的引用计数器属性。用于记录对象被引用的情况:对于一个对象 A , 只要有任何一个对象引用了 A , 则 A 的引用计数器就加 1 ; 当引用失效时, 引用计数器就减 1 。只要对象 A 的引用计数器的值为 0 , 即表示对象 A 不可能再被使用, 可进行回收。 优点: 实现简单, 垃圾对象便于辨识; 判定效率高, 回收没有延迟性。
缺点:
它需要单独的字段存储计数器, 这样的做增加了存储空间的开销。
每次赋值都需要更新计数器, 伴随着加法和减法操作, 这增加了时间开销。
引用计数器有一个严重的问题, 即无法处理循环引用的情况: 这是一条致命缺陷,导致在 Java 的垃圾回收器中没有使用这类算法。见下例:
1.1 java 中未使用 rc 算法证明
调用 System.gc() 后,内存被回收了。 rc 均等于 1,如果使用的是引用计数算法,不应该被回收。 实时上被回收了,所以,证明 JDK 未使用此算法。
1.2 rc 算法算法总结 引用计数算法, 是很多语言的资源回收选择, 例如因人工智能而更加火热的 Python。 具体哪种最优是要看场景的, 业界有大规模实践中仅保留引用计数机制,以提高吞吐量的尝试。 Java 并没有选择引用计数, 是因为其存在一个基本的难题, 也就是很难处理循环引用关系。 Python 如何解决循环引用? 手动解除: 很好理解, 就是在合适的时机 (有一个出发条件), 解除引用关系。 使用弱引用 weakref , weakref 是 python 提供的标准库, 旨在解决循环引用。 2. 可达性分析算法 可达性分析算法(或叫根搜索算法、追踪性垃圾收集)
相对于引用计数算法而言, 可达性分析算法不仅同样具备实现简单和执行高效等特点, 更重要的是该算法可以有效地解决在引用计数算法中循环引用的问题, 防止内存泄漏的发生。 相较于引用计数算法, 这里的可达性分析就是 Java 、C# 选择的。这种类型的垃圾收集通常也叫作追踪性垃圾收集 (Tracing Garbage Collection) 。 可达性分析算法的基本思路:
可达性分析算法是以根对象集合( GC Roots) 为起始点, 按照从上至下的方式搜索被根对象集合所连接的目标对象是否可达。 使用可达性分析算法后, 内存中的存活对象会被根对象集合直接或间接连接着, 搜索所走过的路径称为引用链 (Reference Chain) 如果目标对象没有任何引用链相连, 则是不可达的, 就意味着该对象己经死亡, 可以标记为垃圾对象。 在可达性分析算法中, 只有能够被根对象集合直接或者间接连接的对象才是存活对象。 下面这个图片,很好的说明了这个问题:
那么,这里就有一个特别重要的问题,gc roots 有什么,或者哪些可以作为 gc roots 的元素? 这个是面试的高频电也是重点?下篇专门说明。
全文完 本文由 简悦 SimpRead 优化,用以提升阅读体验 使用了 全新的简悦词法分析引擎 beta,点击查看详细说明
- 引用计数法 1.1 java 中未使用 rc 算法证明 1.2 rc 算法算法总结
- 可达性分析算法