垃圾回收的基本思想是观察每一个对象的可达性,即从根节点开始是否可以访问到这个对象,如果可以,说明当前对象正在被使用,如果从所有的根节点都无法访问到某个对象,说明对象已经不再使用。一般来说,不可达的对象需要被回收,但此对象可能在某个条件下复活自己,如果这样,对此对象的回收就是不合理的。因此需要给出一个对象可触及性状态的定义,并规定在什么状态下才可以安全地回收对象。
可触及性包含以下3种状态。
可触及的:从根节点开始,可以到达这个对象;
可复活的:对象的所有引用被释放即对象是不可达的,但是有可能在finalize()方法中复活;
不可触及的:不可达对象的finalize()方法被调用,并且没有复活,就会进入不可触及状态,不可触及的对象不可能被复活,因为finalize()方法只会被调用1次。
只有在不可触及状态下的对象才能被回收。
1、对象的复活
如果对象在finalize()方法中重新与引用链上的任一对象建立联系,将会复活。推荐在finally语句块中而不是在finalize()方法中释放资源,一是有可能发生引用外泄,无意中复活对象,二是finalize()方法被系统调用的时间是不确定的。
2、引用和可触及性强度
Java提供了强、软、弱、虚4种级别的引用。
强引用:如Object obj = new Object(),强引用能直接访问目标对象,强引用对象是可触及的,任何情况都不会被回收(虚拟机宁愿抛出OOM异常也不会回收),因此可能导致内存泄漏;
软引用:由java.lang.ref.SoftReference类实现,软引用对象在系统发生内存溢出前才会被回收,因此不会导致内存泄露;
弱引用:由java.lang.ref.WeakReference类实现,弱引用对象只要系统发生GC就会被回收,但由于垃圾回收器的线程优先级很低,不一定能很快地发现弱引用对象,因此弱引用对象可能存在较长时间;
虚引用:由java.lang.ref.PhantomReference类实现,无法通过虚引用的get()方法获取对象实例,虚引用必须和引用队列一起使用,用于跟踪垃圾回收过程,虚引用对象在被回收后会将虚引用加入引用队列,用于通知应用程序对象的回收情况。
软引用、弱引用非常适合保存那些可有可无的缓存数据,系统内存不足时,这些缓存数据会被回收,不会导致内存溢出。系统资源充足时,这些缓存数据可以存在较长时间,从而起到加速系统的作用。由于虚引用可以跟踪对象的回收情况,因此可以将一些资源释放操作放在虚引用中执行和记录。
3、Stop The World
大部分情况下,进行对象的可触及性分析时,在整个分析期间不可以出现对象引用关系变化的情况,因此必须停顿所有的Java执行线程(STW)。