对象已死的判定
引用技术算法:
在对象中添加一个引用计数器,每当一个新的引用指向对象时就将引用计数器+1,当引用失效时就-1,最后为0的就是垃圾。引用计数法有着明显的缺陷,当出现以下相互引用的代码时由于A和B相互引用了一次,引用计数器永远不会置零,则堆中将存在无法被回收的垃圾。
User A = new User();
User B = new User();
A = B;
B = A;
A = null;
B = null;
可达性分析算法
Java采用的是可达性分析算法,基本思路是通过一系列Gc Roots的跟对象作为启示节点集,根据这个节点开始向下进行引用关系搜索,搜索所走过的路径为引用链,如果某个对象到到Gc Root 没有引用链则表示这个对象为垃圾。
在Java体系中可作为Gc Roots的对象分别:虚拟机栈中的引用对象、方法区中静态属性引用对象、常用引用对象、本地方法栈中native方法引用对象、Java虚拟机中一些常驻的异常对象、类加载器、同步锁持有对象、反应Java虚拟机内部情况的JMXBean、JVMTI中注册回调和本地代码缓存等。
即使可达性算法判定对象是垃圾,对象也不是非死不可。要判定一个对象死亡最多会进行两次标记,没有与Gc Roots 连接的引用链为第一次标记,随后进行一次筛选,筛选条件为此对象是否有必要执行finalize方法,加入对象没有覆盖finalize方法,或者此方法已经被调用,那么虚拟机将这两种情况都视作没必要清理。
如果这个对象被判定需要执行finalize方法,那么该对象会被放置在一个名为F-Queue的队列中,稍有由一条低优先级的Finalizer线程去运行。如果对象想要自救,需要在finalize方法中与Gc Roots 重新建立引用链。对象只能自救一次,因为任何一个对象的finalize方法都只有被调用一次,下一次回收finalize不回再次执行。
分代理论假说
弱分代假说:绝大多数对象都是朝生夕灭的
强分代假说:熬过越多次垃圾回收的过程的对象就越难消亡
对象的年龄通过熬过垃圾回收的次数决定
Java垃圾回收器应该将堆划分出不同的区域,依照对象的年龄进行存储。如果一个区域的对象都是新生代对象朝生夕灭,每次回收只考虑保存少量的对象,而不是去标记大量需要回收的对象。如果一个区域的对象是老年代对象,那么应该采用较低的频率来回收这块内存区域,同时兼顾垃圾收集时间开销和空间有效利用。
标记清楚算法
首先标记处所有要回收的对象,在回收过程中统一回收标记的对象,当然也可以反过来。
缺点:效率步稳定,会产生大量的空间碎片
标记复制算法
将可用内存划分为大小相等的两块,每次只使用其中一块,当一块内存使用完后将存活对象复制到另一块内存区域,再将已使用的内存清理。一般不在老年代使用。
缺点:内存只能使用一半,空间浪费,暂停用户线程
标记整理算法
与标记复制算法前置相同,不过不进行复制而是将清理后的空间碎片向一段进行整理。
缺点:暂停用户线程,操作负重大
每种回收方法都有一定的缺陷,此外还有一种和稀泥的解决方案,平时采用标记清理,定期采用标记整理进行垃圾回收。