如何判断对象可以回收?
-
引用计数算法
记录每个对象被引用的次数,当引用次数为0时,对象不会再被使用。
引用计数算法最致命的问题是,无法解决对象之间相互循环引用的情况。例如下图,A和B两个对象引用计数均不为0,为1。但实际上却已经不可能被访问到了。
两个对象之间相互循环引用 -
可达性分析
从虚拟机中被称为“GC Roots”的根对象作为起始点,沿着引用的路径向下搜索,基于图论理论,遍历整个引用的图,最终凡是不可达的对象均是不可能再被使用的。
按照Java虚拟机运行时内存分配规则,“GC Roots”显然包括:- 各个线程的虚拟机栈中引用到的对象;
- 本地方法栈中JNI引用的对象;
- 共享内存方法区中类静态属性引用的对象,常量引用的对象;
- 虚拟机内部的引用;
- 被同步锁持有的对象等;
- 反映Java虚拟机内部情况的JMXBean、JVMTI中注册的回调、本地代码缓存等;
除了以上固定之外,垃圾回收器和回收内存区域不同,可以选入一些临时的GC Roots。
Java的引用
JDK1.2之后对Java引用进行了扩充,分为强引用、软引用、弱引用和虚引用。
强引用>软引用>弱引用>虚引用
- 强引用(Strongly Reference),传统的引用赋值,只要存在,垃圾回收器就不会回收对象;
System.out.println("-------Strongly Reference--------");
int[] t1 = new int[10240000];
System.gc();
强引用GC日志
日志中可以看到在Full GC之后,对象仍旧存在。
- 软引用(Soft Reference),在将要发生内存溢出前,会对软引用对象进行二次回收;
System.out.println("-------Soft Reference-----------");
SoftReference t2 = new SoftReference<>(new int[10240000]);
System.gc();
软引用GC日志
Full GC之后软引用的对象仍旧存在。但是如果连续分配大内存对象,在分配失败,内存溢出前会进行清理。
System.out.println("-------Soft Reference-----------");
SoftReference t2 = new SoftReference<>(new int[512000000]);
SoftReference t3 = new SoftReference<>(new int[512000000]);
System.gc();
GC清理软引用
- 弱引用(Weak Reference),关联的对象只能生存到下一次垃圾回收时,无论系统是否有足够的空间,当下一次垃圾回收开始时,关联的对象都会被回收;
System.out.println("-------Weak Reference-----------");
WeakReference t3 = new WeakReference<>(new int[10240000]);
System.gc();
弱引用被回收。
弱引用GC日志
- 虚引用(Phantom Reference),不会对关联对象生存时间产生影响,无法通过虚引用获取对象实例,虚引用的唯一目的就是在这个对象被回收时可以收到一个系统的通知。
对象可以自救的finalize函数,但不建议使用
finalize()函数是对象自救的最后一次机会,覆盖finalize()函数,虚拟机会执行这个方法。如果没有覆盖,或者finalize()函数已经执行过,则虚拟机不会再一次执行。finalize()方法只会被系统自动调用一次。
finalize()函数所能做的工作,try-finally或者其他方法都可以实现,不建议使用finalize()函数。
网友评论