垃圾收集器
垃圾收集器需要完成三件事情
- 哪些内存需要回收
- 什么时候回收
- 如何回收
哪些内存需要回收
判断对象是否存活
- 引用计数法(不是主流)
- 算法:在对象中添加一个引用计数器,每当有一个地方引用它时,计数器值就加一,当引用失效时,计数器值就减一。任何时刻计数器为零的对象就是不可能再被使用的。
- 优点:原理简单,判定效率也很高。
- 缺点:很多例外情况需要考虑,要配合大量额外处理才能保证正确地工作,如该算法就很难解决对象之间相互循环引用的问题。
- 可达性分析法
- 通过一系列称为“GC Roots”的根对象作为初始节点集,从这些节点开始,根据引用关系向下搜索,搜索过程所走过的路径称为“引用链”(Reference Chain),如果某个对象到GC Roots间没有任何引用链相连,或者从GC Roots到这个对象不可达时,则证明此对象不肯能再被使用。
引用的概念
- JDK1.2版之前:如果reference类型的数据中存储的数值代表的是另一块内存的起始地址,就称该reference数据是代表某块内存、某个对象的引用。
- JDK1.2版之后:将引用分为强引用、软引用、弱引用和虚引用。P71
- 强引用:指在程序中普遍存在的引用赋值,即类似“Object obj = new Object()",无论再任何情况下,只要强引用关系还存在,垃圾收集器就永远不会回收掉被引用的对象。
- 软引用:描述一些还有用,但非必须的对象。
- 弱引用也是描述那些非必须对象,但是它的强度比软引用更弱一些,被弱引用关联的对象只能生存到下一次垃圾收集发生为止。
- 虚引用是最弱的一种引用关系。为一个对象设置虚引用关联的唯一目的只是为了能在这个对象被收集器回收时收到一个系统通知。
回收过程
两次标记过程:
1)如果对象在进行可达性分析后发现没有与GC Roots相连的引用链,将会被第一次标记
2)进行筛选,条件是对象是否有必要执行finalize()方法。(在finalize()中重新与引用链上任何一个对象建立关联可以使对象不被回收)
PS:任何一个对象的finalize()方法都只会被系统自动调用一次,如果对象面临下一次回收,该方法不会再被执行。
方法区回收
方法区的垃圾收集主要回收两部分内容:废弃的常量和不再使用的类型。
垃圾收集算法
分类
引用计数式垃圾收集:直接收集
追踪式垃圾收集:间接收集
分代假说里理论
1)弱分代假说:绝大多数对象都是朝生夕灭的。
2)强分代假说:熬过多次垃圾收集过程的对象就越难以消亡。
- 奠定了收集器设计原则:将Java堆划分出不同的区域。
- 每次回收标记少量存活的对象(新生代)
- 把难以消亡的对象放在一块,使用较低频率来回收这个区域(老年代)
3)跨代引用假说:跨代引用相对于同代引用来说仅占极少数。(解决方法:新生代上建立一个全局的数据结构“记忆集”,把老年代划分为若干小块,标识出老年代哪一块内存会存在跨代引用。)
算法:
标记-清除算法
标记-复制算法
标记-整理算法
HotSpot的算法细节实现P81
- 根节点枚举
- 所有收集器在根节点枚举这一步骤时都是必须暂停用户线程,“Stop The World”
- 解决方案:OopMap:虚拟机会把对象内什么偏移量上是什么类型的数据计算出来,在即时编译的过程中也会在
特定的位置
记录下栈里和寄存器里哪些位置是引用。=》总结:记录引用 - 安全点:特定的位置记录了这些信息,这些位置被称为安全点。=》停顿用户线程,让虚拟机进入垃圾回收状态。
- 选取标准:是否具有让程序长时间执行的特征。如:方法调用、循环跳转、异常跳转等
- 如何在垃圾收集发生时让所有线程都跑到最近的安全点,然后停顿下来。
- 抢先式中断:垃圾收集发生时,中断所有线程,恢复不在安全点的线程,再中断。
- 主动式中断:设置中断标志位,线程不断去轮询这个标志,一旦标志位位真就在最近的安全点上主动中断挂起。
- 安全区域
- 确保再某一段代码之中,引用关系不会发生变化。
- 场景:一些用户线程处于Sleep或Block状态,无法走到安全点,应设立一个安全区域。
- 记忆集与卡表(跨代引用问题=》缩减GC Roots的扫描范围)
- 记忆集指从非收集区域指向收集区域的指针集合的抽象数据结构。
- 方案:分区标记思想,不需要标记非收集区域的全部。
- 参考跨代引用假说:跨代引用相对于同代引用来说仅占极少数。
- 字节精度
- 对象精度
- 卡精度
- 卡表
- CARD_TABLE[this address >> 9] = 0;//字节数组
- 以2的9次方,512字节作为卡页划分区域
- 标识1表示元素变脏,卡业内存在跨代指针
- 写屏障(解决卡表元素维护问题)
- AOP思想,在引用对象赋值时产生一个环形通知。
- “伪共享”问题=》更新卡表时写入同一个缓存行而影响性能。
- 方案:采用无条件写屏障,先检查卡表标记,再进行标记
- 并发的可达性分析
- 三色标记
- 白色:尚未被垃圾收集器访问过
- 黑色:已被垃圾收集器访问过,且这个对象的所有引用都已经扫描过
- 灰色:已被垃圾收集器访问过,但这个对象上至少存在一个引用还没被扫描过
- 并发过程可能会产生的后果
- 原本消亡的对象标记为存活
- 原本存活的对象标记为消亡(会导致程序错误)示意图如下
- 解决方案
- 增量更新:黑色对象一旦插入了指向白色对象的引用后,黑色会变成灰色,需要重新扫描。
- 原始快照:无论引用关系删除与否,都会按照刚开始扫描那一刻的对象图快照进行搜索。
- 三色标记