小白一枚,请多指教
小白今日任务:了解Java中的四种引用
1、强应用
强应用是Java中普遍性的引用,覆盖我们编码中大部分场景,比如new出的一个普通对象都属于强引用,作为普通的程序员可能编程生涯基本就跟强引用打交道,下面是一个强引用的例子:
@Test public void referenceDemo() { Object o = new Object(); System.out.println("before gc is "+ o); // 手动调起GC System.gc(); System.out.println("after gc is "+ o); try { // 手动调节堆内存,让程序OOM异常 -Xms10m -Xmx10m byte[] bytes = new byte[11 * 1024 * 1024]; } catch (Exception e) { e.printStackTrace(); } finally { System.out.println("after oom is "+ o); } }
运行得到结果(程序中模拟了GC和OOM异常情况下的引用情况)
before gc is java.lang.Object@382db087 after gc is java.lang.Object@382db087 after oom is java.lang.Object@382db087 Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
可以发现在强引用下,GC不会回收强引用,JVM即使发生OutOfMemoryError也不会试图回收强引用对象来腾出空间
2、软应用
软引用较强引用弱一级,在JVM在内存不足时会试图回收软引用的对象来解决内存不足,软引用的应用场景,可以参考软引用的Java源码注释:
/** * Soft reference objects, which are cleared at the discretion of the garbage * collector in response to memory demand. Soft references are most often used * to implement memory-sensitive caches. *
可以看到软引用一般用于实现内存敏感的缓存中( most often used to implement memory-sensitive caches.),因为缓存的应用场景为:缓存常用数据,加速数据操作,但是在内存不足时可消减,正好符合软引用的定位,下面是软引用的例子:
@Test public void softReferenceDemo() { Object o = new Object(); SoftReference<Object> softReference = new SoftReference<>(o); // 解除强引用,目前只有一个软引用对象 o = null; System.out.println("before gc is "+ softReference.get()); // 手动调起GC System.gc(); System.out.println("after gc is "+ softReference.get()); try { // 手动调节堆内存,让程序OOM异常 -Xms10m -Xmx10m byte[] bytes = new byte[11 * 1024 * 1024]; } catch (Exception e) { e.printStackTrace(); } finally { System.out.println("after oom is "+ softReference.get()); } }
运行得到结果(程序中模拟了GC和OOM异常情况下的引用情况)
before gc is java.lang.Object@382db087 after gc is java.lang.Object@382db087 after oom is null Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
可以发现在软引用下,内存正常时,GC不会回收软引用对象,但是OutOfMemoryError时回收了对象用来解决内存不足
3、弱应用
弱引用较软引用弱一级,GC时只要扫描到弱引用就会回收释放空间,应用场景与软引用类似,可以参考源码中的注释:
/** * Weak reference objects, which do not prevent their referents from being * made finalizable, finalized, and then reclaimed. Weak references are most * often used to implement canonicalizing mappings. *
可以看到弱引用一般用于实现一些规范化的映射关系(often used to implement canonicalizing mappings.),也就是缓存场景,下面时弱引用的例子:
@Test public void weakReference() { Object o = new Object(); WeakReference<Object> weakReference = new WeakReference<>(o); // 解除强引用,目前只有一个弱引用对象 o = null; System.out.println("before gc is "+ weakReference.get()); // 手动调起GC System.gc(); System.out.println("after gc is "+ weakReference.get()); }
运行得到结果(程序中模拟了GC下的引用情况)
before gc is java.lang.Object@382db087 after gc is null
可以发现在弱引用下,即使内存正常,GC也会回收掉弱引用对象
上面说到"规范化的映射关系"也就引申出了 WeakHashMap,用法与HashMap基本一致,GC时会释放,是一般缓存的具体底层实现
4、虚应用
与其他引用不同,虚引用的存在不是为了存储,因为如它的名字一样他几乎在JVM中形同虚设,参照源码中的注释:
/** * Phantom reference objects, which are enqueued after the collector * determines that their referents may otherwise be reclaimed. Phantom * references are most often used for scheduling pre-mortem cleanup actions in * a more flexible way than is possible with the Java finalization mechanism.
从源码注释中可以看到,虚引用会被回收到队列中,构造PhantomReference时也可以发现PhantomReference必须要一个队列参数,具体就是 ReferenceQueue,常用来安排一些回收前的清理操作(most often used for scheduling pre-mortem cleanup actions),实际中几乎用不到,但是可以作为一个知识扩展,下面可以通过一个例子演示:
@Test public void phantomReference() { Object o = new Object(); ReferenceQueue referenceQueue = new ReferenceQueue(); PhantomReference<Object> phantomReference = new PhantomReference<>(o, referenceQueue); // 解除强引用,目前只有一个虚引用对象 o = null; System.out.println("before gc phantomReference is "+ phantomReference.get()); System.out.println("before gc referenceQueue is "+ referenceQueue.poll()); // 手动调起GC System.gc(); System.out.println("after gc phantomReference is "+ phantomReference.get()); System.out.println("after gc referenceQueue is "+ referenceQueue.poll()); }
运行得到结果(程序中模拟了GC下的引用情况)
before gc phantomReference is null before gc referenceQueue is null after gc phantomReference is null after gc referenceQueue is java.lang.ref.PhantomReference@382db087
可以看出虚引用无论GC与否都处于“虚无状态”,并且在GC后会被放入ReferenceQueue队列中,可以通过操作ReferenceQueue队列灵活的进行一些清理工作