即使在可达性分析算法中不可达的对象,也并非是必须被回收,可以缓一缓,对象可以进行一次自救。在垃圾回收清理内存之前先调用一次finalize()方法。
public class FinalizeEscapeGC { public static FinalizeEscapeGC SAVE_HOOK=null; public void isAlive(){ System.out.println("yes, i am still alive"); } @Override protected void finalize() throws Throwable{ super.finalize(); System.out.println("finalize method executed!"); //对象重新建立 GC root 连接链 FinalizeEscapeGC.SAVE_HOOK=this; } public static void main(String[] args) throws Throwable{ SAVE_HOOK = new FinalizeEscapeGC(); //对象第一一次成功拯救自己; SAVE_HOOK =null; System.gc(); //因为fianlize方法优先级很低,所以暂停0.5秒等待他 Thread.sleep(500); if(SAVE_HOOK!=null){ SAVE_HOOK.isAlive(); }else{ System.out.println("no,i am dead :("); } //下面这段代码与上面的完全相同,但是这次自救却失败了;-------------------finalize方法只会被调用一次; SAVE_HOOK =null; System.gc(); //因为fianlize方法优先级很低,所以暂停0.5秒等待他 Thread.sleep(500); if(SAVE_HOOK!=null){ SAVE_HOOK.isAlive(); }else{ System.out.println("no,i am dead :("); } } } 对象自救描述:1、判断是否存在对象与GC Root的reference chain:
如果引用链存在:回收内存;
2、如果不存在:第一次进行标记,并且进行筛选(筛选条件:是否有必要执行finalize()):
没必要执行:当对象没有覆盖finalize()或finalize()方法已经被调用过一次,回收内存;
3、有必要执行:将对象放到F-Queue的队列中,自动创建线程finalizer,第二次小规模标记;
finalize()未执行完,自救失败:回收内存
4、建立引用链,自救成功:不回收
不足:两个过程,效率不高;
清除之后会产生大量不连续空间碎片,从而导致在程序运行时,无法找到足够连续内存,不容易给大对象分配空间,而出发另一次垃圾收集动作。
标记-清除算法是算法的基础,因为这个算法有以上不足,以下算法都是针对这些特点进行修改和优化。
复制算法(copying)
不足:对象存活率较高时,频繁进行复制操作,效率变低;如果不想浪费50%的空间,就必须额外的空间进行分配担保;
标记-整理算法(Mark-Compact)
和标记-清理算法标记过程相同,但不会直接清理掉对象,让所有存活的对象向一端移动,直接清理掉端边界外的内存。
分代收集算法
将java堆分为新生代和老年代,新生代根据其特点,使用复制算法,只需付出少量存活对象复制的成本,而老年代特点是对象存活率时间长,没有额外空间做担保,只能选择标记-清理或标记整理