Chapter 3.1-3.2 - 概述 && 对象已死
Created by : Mr Dk.
2020 / 01 / 25 16:24 🧨🧧
Ningbo, Zhejiang, China
3.1 概述
程序计数器、虚拟机栈、本地方法栈随线程而生,随线程而灭,因此这些区域的内存分配和回收具有确定性,所以不需要过多考虑内存回收的问题。而 Java Heap 和方法区这两个部分有着很显著的不确定性,内存的分配和回收是动态的。
3.2 对象已死?
垃圾收集器对堆进行回收前,首先要确定对象是否还存活或是死去。
3.2.1 引用计数算法
在对象中添加一个引用计数器
- 有一个地方引用它时,计数器 +1
- 引用失效时,计数器 -1
任何时刻,计数器为 0 的对象就是不可能再被使用的。
这个方法存在互相循环引用的问题:
- 两个对象互相引用对方,计数器都不为零
- 但两个对象已经不可能再被访问
- GC 也无法回收这两个对象
3.2.2 可达性分析 (Reachability Analysis) 算法
以 GC Roots 作为根对象 (起点集),顺着引用关系进行搜索。搜索过的路径称为 引用链 (Reference Chain)。如果某个对象到 GC Roots 之间没有任何引用链,就说明该对象不可能再被使用。
3.2.3 再谈引用
引用只有 被引用 和 未被引用 两个状态过于绝对。JDK 1.2 之后,对引用概念进行了补充:
- 强引用 (Strong Reference) 是最传统的引用定义:只要强引用关系还存在,GC 就永远不会回收掉被引用的对象
- 软引用 (Soft Reference) 用于描述一些还有用但非必须的对象:在内存溢出异常前,会对这些对象进行回收,如果回收后还是没有足够的内存,才会抛出内存溢出异常
- 弱引用 (Weak Reference):只能生存到下一次 GC 发生前
- 虚引用 (Phantom Reference):对象在被 GC 时能收到一个系统通知
3.2.4 生存还是死亡?
宣告一个对象死亡的过程
- 对象在进行可达性分析后发现没有与 GC Roots 相连,就被第一次标记,随后进行一次筛选
- 筛选条件是,是否需要为对象执行
finalize()
函数- 一个对象的
finalize()
函数只能被调用一次 - 对象需要覆盖
finalize()
函数才会被调用
- 一个对象的
- 如果有必要执行
finalize()
函数,则对象加入 F-Queue 队列中- 由 JVM 中的一个低调度优先级的线程来执行
- 在
finalize()
中如果能够与引用链建立关系,就能避免被 GC
- 筛选条件是,是否需要为对象执行
- 垃圾收集器对 F-Queue 中的对象进行第二次标记
- 如果对象重新建立引用关系,就移除待 GC 集合
- 剩下的对象会被最终 GC
注意,同一个对象的 finalize()
不会被调用两次,官方声明尽量避免使用 finalize()
。
3.2.5 回收方法区
在方法区回收垃圾的性价比较低。回收对象:
- 废弃的常量
- 不再使用的类型