判断对象是否需要回收
引用计数算法
判断对象是否被其他对象所引用,当对象未被引用时,可被回收
实现方式:给对象添加一个引用计数器,当有地方引用时计数器就加1
相互循环引用
可达性分析算法
从根节点开始,判断对象是否可达,若不可达,则被判定为可回收对象
引用
有一类对象在堆内存充足时继续留在内存中,当堆内存不够时进行GC回收
- 强引用:强应用在GC时永远不会被回收
- 软引用:有用但是非必须的对象。在发生内存溢出前会将内存标记进行二次回收。若回收完还是内存不够则outofmemory
- 弱引用:弱引用用来描述非必要对象。即需要回收对象,若引用在下一次GC时会被回收
- 虚引用:
回收方法区
方法区的回收主要对废弃常量和无用类进行回收。
废弃常量:当前系统中没有任何一个同名且类型系统的常量
无用类:1、所有实例都被回收2、父加载器被回收3、对象未被引用4、无反射访问该类的方法
内存回收算法
标记清除
标记所有需要回收的对象,然后清除
不足:
- 效率不高
- 会产生不连续的内存碎片
标记整理
复制收集算法针对内存存活率高,一直存活的对象来说,频繁复制会降低效率。针对老年代,更适合标记整理算法
先标记需要回收的对象,然后将标记后的对象整理成连续的对象,将不需要的对象清除
复制算法
将内存划分为两块内存一样的内存,每次使用其中一块,当内存使用完时,将活着的对象复制到另一块,然后将使用过得内存一次性清理。
商业虚拟机内存回收
- 将内存中划分为Eden空间和两块较小的survivor空间,每次使用Eden和其中一块survivor。当回收时,将Eden和survivor空间中的对象一次性复制到另一块survivor,最后清理Eden空间和survivor空间
- Hotspot虚拟机Eden和survivor空间内存比例为8:1,当复制的时候survivor空降内存不够时,将会触发分配担保
- **分配担保:**如果一块survivor空降中内存不够时,复制的对象将会通过分配担保机制将对象放入老年代。
分代算法
将java堆分为年轻代和老年代。年轻代采用复制清除算法,老年代使用复制整理算法或者标记清除算法
Hotspot垃圾回收算法实现
枚举根节点
- 可作为根节点的对:虚拟机栈中引用的对象、方法区中常量引用的对象、方法区中常量引用的对象、本地方法栈中引用的对象
- 通过可达性分析从GC ROOT中找引用的对象,可达性分析执行过程中需要停顿所有java线程
安全点
- 通过可达性分析和oopmap快速标记需要回收的对象
- 任何一条语句的执行并不会生成oopmap,在特定的位置记录信息,这些位置称为安全点
- 安全点的选定以程序是否具有让程序长时间执行的特性。长时间执行的最明显特征就是指令序列复用,例如:循环跳转、方法调用、异常跳转等,会产生安全点。
如何在GC时让所有线程跑到安全点附近:抢先中断和主动中断 - 抢先中断:不需要线程的执行代码主动配合,发生GC时,把所有线程全部中断,如果有线程不在安全点时,则恢复线程,让线程跑到安全点上,目前没有虚拟机采用这种方式
- 主动中断:不需要对线程进行操作,需要GC时设置一个标志,线程轮询,发现标记为真时主动挂起。
安全区域
为解决部分线程已经被挂起,无法进入安全点,通过安全区域解决
安全区域为一段代码中引用关系不会发生变化,在这个区域任何位置开始GC都是安全的。