在 JDK.1.2
之后,Java
对引用的概念进行了扩充,将引用分为了:
强引用(Strong Reference)
软引用(Soft Reference)
弱引用(Weak Reference)
虚引用(Phantom Reference)
4 种,这 4 种引用的强度依次减弱。不同的引用在垃圾回收中体现也是不一样~
M 我们先创建一个M对象,后面为了方便的感受GC的情况。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 package com.cyblogs.java.learning.C001_ReferenceType;public class M { @Override public void finalize () { System.out.println("finalize" ); } }
finalize
函数是对象在gc
的时候,一定会调用该方法。我们重写一下该方法并且打印一行日志。
强引用 只要强引用存在,垃圾回收器将永远不会回收被引用的对象,哪怕内存不足时,JVM
也会直接抛出OutOfMemoryError
,不会去回收。如果想中断强引用与对象之间的联系,可以显示的将强引用赋值为null
,这样一来,JVM
就可以适时的回收对象了。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 package com.cyblogs.java.learning.C001_ReferenceType;import java.io.IOException;public class C001_01_NormalReference { public static void main (String[] args) throws IOException { M m = new M (); m = null ; System.gc(); System.in.read(); } }
控制台日志输出:
1 2 3 Connected to the target VM, address: '127.0.0.1:53621', transport: 'socket' finalize Disconnected from the target VM, address: '127.0.0.1:53621', transport: 'socket'
软引用 软引用是用来描述一些非必需但仍有用的对象。在内存足够的时候,软引用对象不会被回收,只有在内存不足时,系统则会回收软引用对象,如果回收了软引用对象之后仍然没有足够的内存,才会抛出内存溢出异常 。这种特性常常被用来实现缓存技术,比如网页缓存,图片缓存等。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 package com.cyblogs.java.learning.C001_ReferenceType;import java.lang.ref.SoftReference;public class C001_02_SoftReference { public static void main (String[] args) throws InterruptedException { SoftReference<byte []> m = new SoftReference <byte []>(new byte [1024 * 1024 * 10 ]); System.out.println(m.get()); System.gc(); Thread.sleep(1000 ); System.out.println(m.get()); byte [] b = new byte [1024 * 1024 * 15 ]; System.out.println(m.get()); } }
因为它是在内存不足的时候才会触发,所以我们在跑之前需要设置一下最大堆。
控制台日志输出:
1 2 3 4 5 Connected to the target VM, address: '127.0.0.1:54335', transport: 'socket' [B@4c3e4790 [B@4c3e4790 null Disconnected from the target VM, address: '127.0.0.1:54335', transport: 'socket'
你会发现就算我们gc
了,后面还是会get
得到,因为空间还足够。当后面byte[] b
再继续申请空间的时候,发现空间不足了,这个时候就会触发gc
动作,把软引用的部分清除掉。
弱引用 弱引用的引用强度比软引用要更弱一些,无论内存是否足够,只要 JVM 开始进行垃圾回收,那些被弱引用关联的对象都会被回收 。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 package com.cyblogs.java.learning.C001_ReferenceType;import java.lang.ref.WeakReference;public class C001_03_WeakReference { public static void main (String[] args) { WeakReference<M> m = new WeakReference <M>(new M ()); System.out.println(m.get()); System.gc(); System.out.println(m.get()); ThreadLocal<M> tl = new ThreadLocal <M>(); tl.set(new M ()); tl.remove(); } }
控制台日志输出:
1 2 3 4 5 Connected to the target VM, address: '127.0.0.1:55151', transport: 'socket' com.cyblogs.java.learning.C001_ReferenceType.M@38cccef null finalize Disconnected from the target VM, address: '127.0.0.1:55151', transport: 'socket'
我们看一下ThreadLocal
的set
方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 public void set (T value) { Thread t = Thread.currentThread(); ThreadLocalMap map = getMap(t); if (map != null ) map.set(this , value); else createMap(t, value); }
为什么Entry
要使用弱引用?
虚引用 虚引用是最弱的一种引用关系,如果一个对象仅持有虚引用,那么它就和没有任何引用一样,它随时可能会被回收,在 JDK1.2 之后,用 PhantomReference 类来表示,通过查看这个类的源码,发现它只有一个构造函数和一个 get() 方法,而且它的 get() 方法仅仅是返回一个null,也就是说将永远无法通过虚引用来获取对象,虚引用必须要和 ReferenceQueue 引用队列一起使用。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 package com.cyblogs.java.learning.C001_ReferenceType;import java.lang.ref.PhantomReference;import java.lang.ref.Reference;import java.lang.ref.ReferenceQueue;import java.util.LinkedList;import java.util.List;public class C001_04_PhantomReference { private static final ReferenceQueue<M> QUEUE = new ReferenceQueue <M>(); private static final List<Object> LIST = new LinkedList <Object>(); public static void main (String[] args) throws InterruptedException { PhantomReference<M> m = new PhantomReference <M>(new M (), QUEUE); new Thread (() -> { while (true ){ LIST.add(new byte [1024 * 1024 ]); try { Thread.sleep(500 ); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(m.get()); } }).start(); new Thread (()->{ while (true ) { Reference<? extends M > poll = QUEUE.poll(); if (poll != null ) { System.out.println("虚引用对象被JVM回收了" + poll); } } }).start(); Thread.sleep(500 ); } }
在零拷贝中就会使用到虚引用,但我们又无法去操作对外的内存。因为太弱了,我们也无法感知到~ 这里就需要利用到ReferenceQueue
。
参考地址