可达性分析🍂
Java 中的对象是通过引用进行指向并访问的
可达性分析, 就是将这些对象被组织的结构视为链式结构
从起始位置出发, 遍历链
能够被访问到的对象标记为"可达"
反之即为"不可达"
(将不可达的作为"垃圾"进行回收)
举个栗子🌰
class TreeNode { int val; TreeNode left; TreeNode right; }
目前所有的节点都是可达的
此时3 → 6之间的连接断开
6不可达
8不可达
于是6, 8被当作垃圾进行回收
- 可达性分析
- 需要进行类似于遍历操作从而判断是否可达
- 相比于引用计数, 其执行效率要慢一些
- 可达性分析的遍历操作, 并不需要一直执行, 是一种周期性的策略(隔一段时间分析一遍)
- 可达性分析遍历的起点
- 栈上的局部变量
- 常量池中的对象
- 静态成员变量
如何清理垃圾
标记清除
将垃圾标记并回收
标记清除的不足🍂
内存碎片问题
被释放的空间是零散的, 不是连续的
申请内存的空间要求是连续的空间
导致总的空闲空间可能很大(非连续), 但每个具体的空间(连续)却较小
从而申请内存失败
复制算法
将内存空间划分为两半
将不是垃圾的对象复制到另一半, 然后将整个空间删除
复制算法解决了内存碎片问题
但也有其不足
复制算法的不足🍂
- 空间利用率较低
- 垃圾少, 有效对象多时, 复制成本较大
标记整理
将垃圾进行标记
将不是垃圾的对象覆盖垃圾的空间
删除剩余空间
解释🍭
1为垃圾
后面的元素依次进行覆盖
由
1, 2, 3, 4, 5, 6
变为
2, 3, 4, 5, 6
解释🍭
3为垃圾
后面的元素依次进行覆盖
由
2, 3, 4, 5, 6
变为
2, 4, 5, 6
解释🍭
5为垃圾
后面的元素依次进行覆盖
由
2, 4, 5, 6
变为
2, 4, 6
解释🍭
删除剩余空间
标记整理解决了复制算法的空间利用率较低的问题
但也有其不足
标记整理的不足🍂
效率问题
(需要将对象进行搬运, 如果要搬运的对象较多, 此时效率就会较低)
分代回收
可以看出, 上述对于垃圾清理的操作, 都有其相对的适用场景与不足
那么, 能不能让合适的垃圾清理方法去其适合的场景呢
于是引出了分代回收
分代回收
将垃圾回收划分成不同的场景
不同的场景应用不同的清理方法
(各展所长)
分代是如何划分的🍭
基于经验规律
是的, 基于经验规律
这个规律是这样的
如果一个东西存在的时间比较长, 那么大概率还会继续长时间的存在下去
这个规律, 对于 Java 的对象也是有效的(由一系列的实验和论证过程得出)
- Java对象的生命周期大致可以划分为两种
- 生命周期极短
- 生命周期极长
于是, 给对象引入了一个概念—年龄
(此处的年龄指代的是熬过GC的轮次, 可以类比于人类的年龄)
于是将堆进行了区域的划分
更为具体的划分
- 新 new 出来的对象, "年龄"为0, 存放在伊甸区
- 使用复制算法
- 熬过一轮 GC 的对象, 存放在幸存区(伊甸区 → 幸存区)
- 使用复制算法
- 在幸存区经过多轮拷贝的对象, 存放在老年代(幸存区 → 老年代)
- 使用标记整理
🔎结尾
创作不易,如果对您有帮助,希望您能点个免费的赞👍
大家有什么不太理解的,可以私信或者评论区留言,一起加油