强引用是使用最广泛的引用。若是一个对象具备强引用,那垃圾回收器毫不会回收它。当内存空间不足,Java虚拟机宁愿抛出OutOfMemoryError错误,使程序异常终止,也不会靠随意回收具备强引用的对象来解决内存不足的问题。 ps:强引用其实也就是咱们平时A a = new A()这个意思。java
<br/> ## Final Reference * 当前类是不是finalizer类,注意这里finalizer是由JVM来标志的( 后面简称f类 ),并非指java.lang.ref.Fianlizer类。可是f类是会被JVM注册到java.lang.ref.Fianlizer类中的。缓存
① 当前类或父类中含有一个参数为空,返回值为void的名为finalize的方法。
② 而且该finalize方法必须非空ide
详见:JVM源码分析之FinalReference彻底解读 - 你假笨源码分析
是用来描述一些还有用但并不是必须的对象。对于软引用关联着的对象,在系统将要发生内存溢出异常以前,将会把这些对象列进回收范围之中进行第二次回收。若是此次回收尚未足够的内存,才会抛出内存溢出异常。 对于软引用关联着的对象,若是内存充足,则垃圾回收器不会回收该对象,若是内存不够了,就会回收这些对象的内存。在 JDK 1.2 以后,提供了 SoftReference 类来实现软引用。软引用可用来实现内存敏感的高速缓存。软引用能够和一个引用队列(ReferenceQueue)联合使用,若是软引用所引用的对象被垃圾回收器回收,Java虚拟机就会把这个软引用加入到与之关联的引用队列中。
注意:Java 垃圾回收器准备对SoftReference所指向的对象进行回收时,调用对象的 finalize() 方法以前,SoftReference对象自身会被加入到这个 ReferenceQueue 对象中,此时能够经过 ReferenceQueue 的 poll() 方法取到它们。性能
/** * 软引用:对于软引用关联着的对象,在系统将要发生内存溢出异常以前,将会把这些对象列进回收范围之中进行第二次回收( 由于是在第一次回收后才会发现内存依旧不充足,才有了这第二次回收 )。若是此次回收尚未足够的内存,才会抛出内存溢出异常。 * 对于软引用关联着的对象,若是内存充足,则垃圾回收器不会回收该对象,若是内存不够了,就会回收这些对象的内存。 * 经过debug发现,软引用在pending状态时,referent就已是null了。 * * 启动参数:-Xmx5m * */ public class SoftReferenceDemo { private static ReferenceQueue<MyObject> queue = new ReferenceQueue<>(); public static void main(String[] args) throws InterruptedException { Thread.sleep(3000); MyObject object = new MyObject(); SoftReference<MyObject> softRef = new SoftReference(object, queue); new Thread(new CheckRefQueue()).start(); object = null; System.gc(); System.out.println("After GC : Soft Get = " + softRef.get()); System.out.println("分配大块内存"); /** * ====================== 控制台打印 ====================== * After GC : Soft Get = I am MyObject. * 分配大块内存 * MyObject's finalize called * Object for softReference is null * After new byte[] : Soft Get = null * ====================== 控制台打印 ====================== * * 总共触发了 3 次 full gc。第一次有System.gc();触发;第二次在在分配new byte[5*1024*740]时触发,而后发现内存不够,因而将softRef列入回收返回,接着进行了第三次full gc。 */ // byte[] b = new byte[5*1024*740]; /** * ====================== 控制台打印 ====================== * After GC : Soft Get = I am MyObject. * 分配大块内存 * Exception in thread "main" java.lang.OutOfMemoryError: Java heap space * at com.bayern.multi_thread.part5.SoftReferenceDemo.main(SoftReferenceDemo.java:21) * MyObject's finalize called * Object for softReference is null * ====================== 控制台打印 ====================== * * 也是触发了 3 次 full gc。第一次有System.gc();触发;第二次在在分配new byte[5*1024*740]时触发,而后发现内存不够,因而将softRef列入回收返回,接着进行了第三次full gc。当第三次 full gc 后发现内存依旧不够用于分配new byte[5*1024*740],则就抛出了OutOfMemoryError异常。 */ byte[] b = new byte[5*1024*790]; System.out.println("After new byte[] : Soft Get = " + softRef.get()); } public static class CheckRefQueue implements Runnable { Reference<MyObject> obj = null; [@Override](https://my.oschina.net/u/1162528) public void run() { try { obj = (Reference<MyObject>) queue.remove(); } catch (InterruptedException e) { e.printStackTrace(); } if (obj != null) { System.out.println("Object for softReference is " + obj.get()); } } } public static class MyObject { [@Override](https://my.oschina.net/u/1162528) protected void finalize() throws Throwable { System.out.println("MyObject's finalize called"); super.finalize(); } [@Override](https://my.oschina.net/u/1162528) public String toString() { return "I am MyObject."; } } }
用来描述非必须的对象,可是它的强度比软引用更弱一些,被弱引用关联的对象只能生存到下一次垃圾收集发生以前。当垃圾收集器工做时,不管当前内存是否足够,都会回收掉只被弱引用关联的对象。一旦一个弱引用对象被垃圾回收器回收,便会加入到一个注册引用队列中。
注意:Java 垃圾回收器准备对WeakReference所指向的对象进行回收时,调用对象的 finalize() 方法以前,WeakReference对象自身会被加入到这个 ReferenceQueue 对象中,此时能够经过 ReferenceQueue 的 poll() 方法取到它们。ui
/** * 用来描述非必须的对象,可是它的强度比软引用更弱一些,被弱引用关联的对象只能生存到下一次垃圾收集发送以前。当垃圾收集器工做时,不管当前内存是否足够,都会回收掉只被弱引用关联的对象。一旦一个弱引用对象被垃圾回收器回收,便会加入到一个注册引用队列中。 */ public class WeakReferenceDemo { private static ReferenceQueue<MyObject> queue = new ReferenceQueue<>(); public static void main(String[] args) { MyObject object = new MyObject(); Reference<MyObject> weakRef = new WeakReference<>(object, queue); System.out.println("建立的弱引用为 : " + weakRef); new Thread(new CheckRefQueue()).start(); object = null; System.out.println("Before GC: Weak Get = " + weakRef.get()); System.gc(); System.out.println("After GC: Weak Get = " + weakRef.get()); /** * ====================== 控制台打印 ====================== * 建立的弱引用为 : java.lang.ref.WeakReference@1d44bcfa * Before GC: Weak Get = I am MyObject * After GC: Weak Get = null * MyObject's finalize called * 删除的弱引用为 : java.lang.ref.WeakReference@1d44bcfa , 获取到的弱引用的对象为 : null * ====================== 控制台打印 ====================== */ } public static class CheckRefQueue implements Runnable { Reference<MyObject> obj = null; @Override public void run() { try { obj = (Reference<MyObject>)queue.remove(); } catch (InterruptedException e) { e.printStackTrace(); } if(obj != null) { System.out.println("删除的弱引用为 : " + obj + " , 获取到的弱引用的对象为 : " + obj.get()); } } } public static class MyObject { @Override protected void finalize() throws Throwable { System.out.println("MyObject's finalize called"); super.finalize(); } @Override public String toString() { return "I am MyObject"; } } }
PhantomReference 是全部“弱引用”中最弱的引用类型。不一样于软引用和弱引用,虚引用没法经过 get() 方法来取得目标对象的强引用从而使用目标对象,观察源码能够发现 get() 被重写为永远返回 null。
那虚引用到底有什么做用?其实虚引用主要被用来 跟踪对象被垃圾回收的状态,经过查看引用队列中是否包含对象所对应的虚引用来判断它是否 即将被垃圾回收,从而采起行动。它并不被期待用来取得目标对象的引用,而目标对象被回收前,它的引用会被放入一个 ReferenceQueue 对象中,从而达到跟踪对象垃圾回收的做用。
当phantomReference被放入队列时,说明referent的finalize()方法已经调用,而且垃圾收集器准备回收它的内存了。
注意:PhantomReference 只有当 Java 垃圾回收器对其所指向的对象真正进行回收时,会将其加入到这个 ReferenceQueue 对象中,这样就能够追综对象的销毁状况。这里referent对象的finalize()方法已经调用过了。 因此具体用法和以前两个有所不一样,它必须传入一个 ReferenceQueue 对象。当虚引用所引用对象准备被垃圾回收时,虚引用会被添加到这个队列中。
Demo1:spa
/** * 虚引用也称为幽灵引用或者幻影引用,它是最弱的一种引用关系。一个持有虚引用的对象,和没有引用几乎是同样的,随时都有可能被垃圾回收器回收。 * 虚引用必须和引用队列一块儿使用,它的做用在于跟踪垃圾回收过程。 * 当phantomReference被放入队列时,说明referent的finalize()方法已经调用,而且垃圾收集器准备回收它的内存了。 */ public class PhantomReferenceDemo { private static ReferenceQueue<MyObject> queue = new ReferenceQueue<>(); public static void main(String[] args) throws InterruptedException { MyObject object = new MyObject(); Reference<MyObject> phanRef = new PhantomReference<>(object, queue); System.out.println("建立的虚拟引用为 : " + phanRef); new Thread(new CheckRefQueue()).start(); object = null; int i = 1; while (true) { System.out.println("第" + i++ + "次GC"); System.gc(); TimeUnit.SECONDS.sleep(1); } /** * ====================== 控制台打印 ====================== * 建立的虚拟引用为 : java.lang.ref.PhantomReference@1d44bcfa * 第1次GC * MyObject's finalize called * 第2次GC * 删除的虚引用为: java.lang.ref.PhantomReference@1d44bcfa , 获取虚引用的对象 : null * ====================== 控制台打印 ====================== * * 再通过一次GC以后,系统找到了垃圾对象,并调用finalize()方法回收内存,但没有当即加入PhantomReference Queue中。由于MyObject对象重写了finalize()方法,而且该方法是一个非空实现,因此这里MyObject也是一个Final Reference。因此地刺GC完成的是Final Reference的事情。 * 第二次GC时,该对象真处理PhantomReference,此时,将PhantomReference加入虚引用队列( PhantomReference Queue )。 * 并且每次gc之间须要停顿一些时间,已给JVM足够的处理时间;若是这里没有TimeUnit.SECONDS.sleep(1); 可能须要gc到第五、6次才会成功。 */ } public static class MyObject { @Override protected void finalize() throws Throwable { System.out.println("MyObject's finalize called"); super.finalize(); } @Override public String toString() { return "I am MyObject"; } } public static class CheckRefQueue implements Runnable { Reference<MyObject> obj = null; @Override public void run() { try { obj = (Reference<MyObject>)queue.remove(); System.out.println("删除的虚引用为: " + obj + " , 获取虚引用的对象 : " + obj.get()); System.exit(0); } catch (InterruptedException e) { e.printStackTrace(); } } } }
Q:👆了解下System.gc()操做,若是连续调用,若前一次没完成,后一次可能会失效,因此链接调用System.gc()其实做用不大?
A:关于上面例子的问题咱们要补充两点
① 首先咱们先来看下System.gc()的doc文档:.net
/** * Runs the garbage collector. * <p> * Calling the <code>gc</code> method suggests that the Java Virtual * Machine expend effort toward recycling unused objects in order to * make the memory they currently occupy available for quick reuse. * When control returns from the method call, the Java Virtual * Machine has made a best effort to reclaim space from all discarded * objects. * <p> * The call <code>System.gc()</code> is effectively equivalent to the * call: * <blockquote><pre> * Runtime.getRuntime().gc() * </pre></blockquote> * * @see java.lang.Runtime#gc() */ public static void gc() { Runtime.getRuntime().gc(); }
当这个方法返回的时候,Java虚拟机已经尽最大努力去回收全部丢弃对象的空间了。
所以不存在这System.gc()操做连续调用时,若前一次没完成,后一次可能会失效的状况。以及“因此链接调用System.gc()其实做用不大”这个说法不对,应该说连续调用System.gc()对性能可定是有影响的,但做用之一就是能够清除“漂浮垃圾”。
② 同时须要特别注意的是对于已经没有地方引用的这些f对象,并不会在最近的那一次gc里立刻回收掉,而是会延迟到下一个或者下几个gc时才被回收,由于执行finalize方法的动做没法在gc过程当中执行,万一finalize方法执行很长呢,因此只能在这个gc周期里将这个垃圾对象从新标活,直到执行完finalize方法将Final Reference从queue里删除,这样下次gc的时候就真的是漂浮垃圾了会被回收。线程
Demo2:debug
public class PhantomReferenceDemo2 { public static void main(String[] args) { ReferenceQueue<MyObject> queue = new ReferenceQueue<>(); MyObject object = new MyObject(); Reference<MyObject> phanRef = new PhantomReference<>(object, queue); System.out.println("建立的虚拟引用为 : " + phanRef); object = null; System.out.println(phanRef.get()); System.gc(); System.out.println("referent : " + phanRef); System.out.println(queue.poll() == phanRef); //true /** * ====================== 控制台打印 ====================== * 建立的虚拟引用为 : java.lang.ref.PhantomReference@1d44bcfa * null * referent : java.lang.ref.PhantomReference@1d44bcfa * true * ====================== 控制台打印 ====================== * * 这里由于MyObject没有重写finalize()方法,因此这里的在System.gc()后就会处理PhantomReference加入到PhantomReference Queue中。 */ } public static class MyObject { @Override public String toString() { return "I am MyObject"; } } }