java gc --- 四种引用

时间:2023-03-09 03:42:26
java gc --- 四种引用

古龙有《七种武器》,java里有四种引用。

下文主要是对 《understanding-weak-references》这一博文的重点进行翻译

强引用,strong references

StringBuffer buffer = new StringBuffer()

像上面这样的就是强引用,只要某个对象与gc root有强引用链相连,那么这个对象就不可能被gc回收掉。jvm宁可发生OOM,也不会回收这个对象。

强引用有时可能太强了

作者提出了两种场景

1. 假设我们有一个不可扩展的Widget类,现在我们想给每个Widget对象附加一个编号属性,那么我们可以这么做:额外搞一个HashMap,key是Widget对象,value是编号,通过更新与查询这个HashMap,可以实现想要的功能。

但是这个时候问题来了,如果有的Widget对象不再被使用到了(成为了垃圾),由于HashMap中保存了对所有Widget的强引用,所以GC绝对不会释放这些Widget,那么这个HashMap就会越来越大,与之关联的Widget也会越来越多,直到发生OOM。

为了解决这个问题,一个很简单的想法当然是“如果有Widget不用了,那就把它从HashMap里删掉啊”,但是如何才能知道某个Widget再不被使用了呢?总不能再弄一个引用计数系统吧。

2. 假设我们正在构建一个缓存系统,比方说把硬盘里的图片缓存到内存里,但是内存总是有限的,如果我们在内存中保存的是图片对象的强引用,那么这些对象不会在gc中被释放,这很容易就会导致内存被耗尽,然后发生OOM

弱引用  Weak References

弱引用不足以让对象在内存中强制驻留。也就是说,被弱引用修饰的对象,在gc过程中可能会被回收,你并不需要什么额外的操作。下面是使用范例:

WeakReference<Widget> weakWidget = new WeakReference<Widget>(widget);

我们可以使用weakWidget.get()方法来获取这个弱引用所指向的对象,但是由于这个对象可能已经被gc回收了,所以get到的值可能为null。

对于上面提出的第一种场景,我们可以使用系统内置的WeakHashMap来完成这一工作,WeakHashMap的特点是它的key值是WeakReference(不是value值!),而且如果WeakHashMap的某个key值被自动回收了,它所对应的value也会被自动释放。场景一的问题就这样被轻松解决了。

引用队列  Reference Queues

如果WeakReference的get方法返回null了,那么这个WeakReference所关联的对象已经被释放了,但是这个WeakReference对象本身还是存在的,它会占用空间,为了避免内存泄漏。我们需要一个机制来确保WeakReference也能被释放。

于是有了ReferenceQueue。

如果在创建WeakReference的时候,在构造函数里传入一个ReferenceQueue,那么在这个WeakReference所引用的对象被回收之后,这个WeakReference会被自动插入到ReferenceQueue里来。于是我们可以在适当的时候(比方说后台开一个定时线程)去扫描这个ReferenceQueue,然后把队列里的无用的WeakReference全部清除掉。

软引用 Soft References

软引用与弱引用极其类似,但是软引用相比之下并不急于释放所引用的对象。

弱引用不会影响gc,弱引用指向的对象只要被gc扫描到,就会被释放,但是由于gc线程的优先级很低,而且这个对象可能已经被提升到了老年代,所以这个对象还是可能存活很久。

软引用会影响gc,被软引用指向的对象只有在内存不足的时候,才会被释放掉(generally retained as long as memory is in plentiful supply)。

我个人的理解就是:软引用指向的对象可以无视young gc,只在full gc里被回收。弱引用的对象碰到young gc也会被回收。

幻引用 Phantom References

幻引用的get方法永远返回null(PhantomReference重写了父类Reference的get方法,其get方法里就一行代码:return null),也就是说一旦一个对象被放到幻引用里,就再也不可能通过这个幻引用找到它了。

它唯一的作用就是在对象被真正回收之后,放到ReferenceQueue里(因此它只有一个带有ReferenceQueue的构造方法)。

它与WeakReference的区别是:

WeakReference:一旦探测到对象只有WeakReference,就将其放入ReferenceQueue(WeakReference are enqueued as soon as the object to which they point becomes weakly reachable. This before finalization or garbage collection has actually happened.)

PhantomReference:只有在对象被真正销毁之后,才会被放入到ReferenceQueue中(PhantomReferences are enqueued only when the object is physically removed from memory.)

很奇怪,网上很多中文资料里写的都是错的。

稍微总结一下,按引用强度来划分:

强引用>软引用>弱引用>幻引用

ps: ReferenceQueue还与FinalReference有相当的关联,具体请查看参考资料中的那篇infoq的文章

参考资料

https://community.oracle.com/blogs/enicholas/2006/05/04/understanding-weak-references

https://www.zhihu.com/question/49760047/answer/123486092

http://www.infoq.com/cn/articles/jvm-source-code-analysis-finalreference