Stay Hungry Stay Foolish

Java对象的终结与可达性

Posted on By Jun Xi Gu

本文是对Java对象的终结与可达性的汇总和个人理解


finalize方法

是什么?

对象可以有一个finalize方法,当垃圾回收器检查到对象成为垃圾时 会调用这个对象的finalize方法;执行外finalize方法后,垃圾回收器需要重新扫描来查找垃圾对象,因为在finalize方法里对象有可能复活了(被其他存活对象引用 变成可达时);但一个对象的finalize方法只会被执行一次,当对象复活后又变成垃圾时,垃圾收集器是不会再调用它的finalize方法的

为什么使用finalize?

一般作为释放非内存资源的最后一道“保护栏”,以防止客户没有明确的释放资源时能由JVM帮忙释放
让JVM帮忙在回收内存的最后时刻释放非内存资源或者本地方法的内存,这些资源必须是非关键、时间不敏感的

缺点

finalize非常底层,依赖JVM的机制,很容易用错,用错时还会导致资源和内存的不及时释放;
一般不用finalize方法,因为它是由垃圾收集器调用的,而垃圾回收的时机是应用不能控制的,具有不确定性;
可以用WeakReference来作为替代方案,请参考文章

对象的可达性

是什么?

在垃圾收集器看来,堆中的对象有6中状态:可达,软可达,弱可达,影子可达,可复活,不可达

  • 可达:普通的引用指向的对象都是可达,垃圾收集器是不会回收 可达对象的
  • 软可达:不是可达并被SoftReference指向的对象是软可达的,垃圾回收器在内存实在不够用时(要抛OutOfMemoryException) 可以 回收软可达对象,并尽量不回收,回收时也先回收更老的对象
  • 弱可达:不是可达或软可达并被WeakReference指向的对象是弱可达的,垃圾回收器 必须 回收弱可达对象
  • 可复活:当垃圾回收器在执行finalize方法时,所有的未被回收的不可达对象都有可能复活,他们就处于可复活状态
  • 影子可达:不是可达,软可达或弱可达且不能被复活,并被PhantomReference指向的对象是影子可达的
  • 不可达:finalize方法执行完后还是不可达的对象是不可达的

SoftReference,WeakReference和PhantomReference是Java的三种引用对象,继承于Reference类,程序可以通过这3种引用来间接的引用一个对象;Reference有个clear方法,用来切断Reference和对象之间的引用了

由于软可达和弱可达的对象可能会被回收,程序为了得到对象已被通知,程序可以为SoftReference和WeakReference对象提供一个队列,当Reference引用的对象被回收时,垃圾收集器调用Reference对象的clear方法并把Reference对象放到队列中,程序可以从队列里取出Reference对象来获知对象已被清除

程序必须为PhantomReference对象提供一个队列,当一个对象成为影子可达时,垃圾收集器会把PhantomReference对象放到队列里,程序从队列里获取PhantomReference对象后调用clear方法后,垃圾收集器才会回收 垃圾对象;当一个对象成为影子可达时,程序是没有办法让这个对象的可达性发生变化

为什么?

通过软可达可以实现内存缓存,当软可达对象未被回收时,可通过Reference的get方法获取对象

通过弱可达可以达到一种效果:使用一些不由程序决定其回收时刻的对象,该对象可能存在也可能已经被回收

通过影子可达可以让程序决定一个对象的释放时刻