Java中强软弱虚四种引用详解
强、软、弱、虚四种引用的引用强度逐渐减弱
一、强引用
-
默认的引用类型(99%以上的引用都是强引用),即类似
object o = new object()
这种引用关系 -
使用new操作符创建一个新的对象,并将其赋值给一个变量的时候,这个变量就成为指向该对象的一个强引用,这个对象成为被强引用的对象
-
如果被强引用的对象正在被直接或间接的引用着,这个对象就永远不会被垃圾回收(即使溢出也不回收,故OOM的异常通常是由强引用造成的)
-
如果强引用被赋予null值或者强引用超出了引用范围,则没有被引用的被强引用对象可以被垃圾回收
-
举两个例子
StringBuffer s1 = new StringBuffer("Hello!"); StringBuffer s2 = s1; //有两个强引用指向了堆中的Hello //堆中的Hello不会被回收 StringBuffer s1 = new StringBuffer("Hello!"); s1 = null; //没有引用指向堆中的Hello //堆中的Hello会被回收
二、软引用
- 软引用的创建和使用
//创建一个字节数组,或者其他类型均可
byte[] M = new byte[1024];
//创建软引用
SoftReference<byte[]> m = new SoftReference<>(M); //见图示
//拿到被软引用的对象
System.out.println(m.get());
//没有重写toString,输出地址值
创建的软引用见下图:
- 软引用的特点
- 当执行完垃圾回收之后内存还不够的情况下被软引用的对象会被回收
- 被软引用的对象被回收之后,软引用会移动至引用队列,通过引用队列将无用的软引用删除
- 也可以不配合引用队列使用
- 软引用常用来当作缓存使用,内存不够时将缓存移出去,需要的时候再加载进来
三、弱引用
-
弱引用的创建
//创建一个字节数组,或者其他类型均可 byte[] M = new byte[1024]; //创建弱引用 WeakReference<byte[]> m = new WeakReference<>(M); //内存结构图与软引用类似,不再赘述
-
弱引用的特点
- 被弱引用的对象当执行垃圾回收时就会被回收
- 被弱引用的对象被回收之后,弱引用会移动至引用队列,通过引用队列将无用的弱引用删除
- 也可以不配合引用队列使用
- 弱引用通常在ThreadLocal中使用,防止内存泄漏
四、虚引用
- 虚引用的创建和使用
//创建一个字节数组,或者其他类型均可
byte[] M = new byte[1024];
//创建一个引用队列
ReferenceQueue<byte[]> queue = new ReferenceQueue<>();
//创建虚引用
PhantomReference<byte[]> phantomReference = new PhantomReference<>(M, queue);
//参数1指定虚引用对象,参数2指定引用队列
System.out.println(phantomReference.get()); //null
//无法通过虚引用获取引用对象(即使没有进行垃圾回收)
//虚引用内存结构图与上述类似,不再赘述
- 虚引用常用来管理直接内存
-
虚引用对象会指向操作系统的直接内存
-
当虚引用对象被GC回收时,同时应该回收其指向的直接内存
-
还没有将虚引用对象完全回收时,虚引用会进入引用队列
-
引用队列中会有一个虚引用指向虚引用对象,如下图
-
自己定义一个线程扫描引用队列,当扫描到虚引用之后,可以找到虚引用对象,从而找到指向的直接内存,从而可以将直接内存回收
-
综上,虚引用只是起一个通知的作用
-
-
虚引用的特点
- 被虚引用的对象当执行垃圾回收时就会被回收
- 虚引用必须配合引用队列使用,虚引用创建时会关联一个引用队列
-
Netty中对虚引用的应用
- ByteBuffer(NIO操作中被虚引用的对象)会传递直接内存地址给虚引用
- ByteBuffer被回收之后,其关联的直接内存不会被 Java回收
- ByteBuffer被回收之后,虚引用会进入引用队列
- ReferenceHandler 线程会定期扫描此引用队列
- 如果扫描到引用队列中有虚引用,通过虚引用Cleaner中的直接内存地址找到直接内存并执行
unsafe.freeMemory()
方法来释放直接内存