前言Java执行GC判断对象是否存活的方式有两种,其中一种是引用计数。引用计数:Java堆中的每个对象都有一个引用计数属性,每增加一个引用计数加1,每释放一个引用计数减1。在JDK1.2之前的版本中,如果一个对象没有被任何变量引用,那么程序就不能再使用这个对象。也就是说,程序只能使用处于可达状态的对象。从JDK1.2开始,对象引用分为四个层次,使程序可以更灵活地控制对象的生命周期。这4个等级从高到低分别是:强参照、软参照、弱参照和虚参照。文本强引用(StrongReference)强引用是最常用的引用。如果一个对象有强引用,垃圾收集器将永远不会收集它。如下:ObjectstrongReference=newObject();当内存空间不足时,Java虚拟机宁愿抛出OutOfMemoryError错误导致程序异常终止,也不会随意回收强引用对象来解决内存不足的问题。如果强引用对象没有被使用,需要将其弱化,以便GC回收,如下:strongReference=null;复制代码显式设置strongReference对象为null,或者让它超过对象的生命周期,那么gc就认为对象不存在Reference,那么对象就可以被回收了。何时收集取决于GC算法。publicvoidtest(){ObjectstrongReference=newObject();//省略其他操作}Copy代码在方法内部有一个强引用,这个引用保存在Java栈中,真正的引用内容(Object)保存在Java堆中。当方法运行结束后,退出方法栈,被引用对象的引用号为0,对象被回收。但是如果这个strongReference是一个全局变量,那么在这个对象不用的时候就需要赋值null,因为强引用不会被垃圾回收。ArrayList的clear方法:在ArrayList类中定义了一个elementData数组。当调用clear方法清除数组时,每个数组元素都被赋予一个null值。与elementData=null不同的是,强引用仍然存在,避免在添加元素并随后调用add()等方法时重新分配内存。特别适合使用clear()方法的内存数组中存储的引用类型来进行内存释放,这样可以及时释放内存。软引用(SoftReference)如果一个对象只有软引用,当内存空间足够时,垃圾回收器不会回收它;如果内存空间不足,就会回收这些对象的内存。只要垃圾收集器不收集它,该对象就可以被程序使用。软引用可用于实现对内存敏感的缓存。//强引用StringstrongReference=newString("abc");//软引用Stringstr=newString("abc");SoftReferencesoftReference=newSoftReference(str);复制代码软引用可以和引用队列(ReferenceQueue)结合使用。如果软引用引用的对象被垃圾回收,JAVA虚拟机会将软引用添加到与其关联的引用队列中。ReferenceQueuereferenceQueue=newReferenceQueue<>();Stringstr=newString("abc");SoftReferencesoftReference=newSoftReference<>(str,referenceQueue);str=null;//通知GCSystem.gc();System.out.println(softReference.get());//abc引用reference=referenceQueue.poll();System.out.println(reference);//空拷贝代码注意:软引用jvm内存不足时会回收对象。我们调用System.gc()方法只是为了通知。JVM扫描时回收的对象由JVM本身的状态决定。即使扫描了一个软引用对象,也不一定会被回收,只有内存不够时才会被回收。当内存不足时,JVM先将软引用中的对象引用设置为null,然后通知垃圾回收器回收:if(JVMinsufficientmemory){//将软引用中的对象引用设置为nullstr=无效的;//通知垃圾回收器回收System.gc();}复制代码也就是说垃圾回收线程会在虚拟机抛出OutOfMemoryError之前回收软引用对象,虚拟机优先回收软引用对象尽可能多地引用已经闲置了很长时间的对象。对于那些刚刚构建或者刚刚使用的“较新”的软对象,虚拟机会尽可能的保留它们,这就是引入引用队列ReferenceQueue的原因。应用场景:浏览器的后退按钮。按返回时,返回时显示的网页内容是重新请求还是从缓存中取出?这取决于具体的实施策略。如果网页在浏览结束时被回收,则需要在按返回查看之前浏览过的页面时重新构建该网页;如果将浏览过的网页存放在内存中,会造成大量的内存浪费,甚至会造成内存溢出。这时候可以使用软引用,很好的解决了实际问题://获取浏览器的浏览器对象Browserbrowser=newBrowser();//从后台程序加载浏览页面BrowserPagepage=browser.getPage();//将浏览过的页面设置为软引用softReferencesoftReference=newSoftReference(page);//返回或再次浏览此页面时if(softReference.get()!=null){//内存充足,未被占用回收器回收,直接获取缓存page=softReference.get();}else{//内存不足,软引用的对象已被回收page=browser.getPage();//重建软引用softReference=newSoftReference(page);}复制代码3.弱引用(WeakReference)弱引用和软引用的区别在于只有弱引用的对象生命周期更短。在垃圾回收线程扫描其管辖内存区域的过程中,一旦发现只有弱引用的对象,无论当前内存空间是否足够,都会回收其内存。然而,由于垃圾收集器是一个非常低优先级的线程,只有弱引用的对象可能无法快速找到。Stringstr=newString("abc");WeakReferenceweakReference=newWeakReference<>(str);海峡=空;copycodeJVM首先将软引用中的对象引用设置为null,然后通知垃圾回收器进行回收:str=null;系统.gc();复制代码注意:如果一个对象是偶尔(很少)使用的,并且你想在使用的时候随时获取它,但又不想影响这个对象的垃圾回收,那么你应该使用WeakReference来记住这个对象。下面的代码将弱引用再次变成强引用:Stringstr=newString("abc");WeakReferenceweakReference=newWeakReference<>(str);//弱引用强引用StringstrongReference=weakReference.get();复制代码同样,弱引用可以与引用队列(ReferenceQueue)结合使用。如果弱引用引用的对象被垃圾回收,Java虚拟机会将这个弱引用添加到队列中与其关联的引用中。简单测试:GCTarget.javapublicclassGCTarget{//对象IDpublicStringid;//占用内存空间byte[]buffer=newbyte[1024];publicGCTarget(Stringid){this.id=id;}protectedvoidfinalize()throwsThrowable{//执行垃圾回收时打印并显示对象IDSystem.out.println("FinalizingGCTarget,idis:"+id);}}复制代码GCTargetWeakReference.javapublicclassGCTargetWeakReferenceextendsWeakReference{//弱引用IDpublicStringid;publicGCTargetWeakReference(GCTargetgcTarget,ReferenceQueuequeue){super(gcTarget,queue);this.id=gcTarget.id;}protectedvoidfinalize(){System.out.println("FinalizingGCTargetWeakReference"+id);}}复制代码WeakReferenceTest.javapublicclassWeakReferenceTest{//弱引用队列privatefinalstaticReferenceQueueREFERENCE_QUEUE=newReferenceQueue<>();publicstaticvoidmain(String[]args){LinkedListgcTargetList=newLinkedList<>();//创建弱引用对象,并一一加入链表for(inti=0;i<5;i++){GCTargetgcTarget=newGCTarget(String.valueOf(i));GCTargetWeakReferenceweakReference=newGCTargetWeakReference(gcTarget,REFERENCE_QUEUE);gcTargetList.add(weakReference);System.out.println("刚刚创建了GCTargetWeakReferenceobj:"+gcTargetList.getLast());}//通知GC进行垃圾回收System.gc();try{//休息几分钟,等待上面的垃圾回收线程完成Thread.sleep(6000);}catch(InterruptedExceptione){e.printStackTrace();}//检查关联的引用队列是否为空Reference参考;while((reference=REFERENCE_QUEUE.poll())!=null){if(referenceinstanceofGCTargetWeakReference){System.out.println("在队列中,id是:"+((GCTargetWeakReference)(reference)).id);}}}}复制代码,运行WeakReferenceTest.java,运行结果如下:可以看出,WeakReference对象的生命周期基本由垃圾回收器控制决定一旦垃圾回收线程发现弱引用对象,就在下一次GC过程中回收。与其他类型的引用不同,虚引用不决定对象的生命周期。如果一个对象只持有虚引用,就好像它没有引用一样,随时可能被垃圾收集器回收。应用场景:幻象引用主要用于跟踪对象被垃圾回收器回收的活动。幻影引用与软引用和弱引用的区别之一是幻影引用必须与引用队列(ReferenceQueue)结合使用。当垃圾回收器要回收一个对象时,如果发现它还有一个虚引用,就会把这个虚引用添加到与之关联的引用队列中,然后再回收该对象的内存。Stringstr=newString("abc");ReferenceQueuequeue=newReferenceQueue();//创建一个幻象引用,需要关联一个引用队列PhantomReferencepr=newPhantomReference(str,queue);拷贝代码程序可以通过判断引用队列中是否加入了幻象引用来获知被引用对象是否即将被垃圾回收。如果程序发现引用队列中加入了虚引用,则可以在被引用对象的内存被回收之前采取必要的动作。总结一下Java中四种引用的等级和强度从高到低:强引用->软引用->弱引用->幻象引用Recycle。垃圾回收器会从根对象Object中标记存活下来的对象,然后回收一些不可达的对象和一些被引用的对象。用表来说明,如下:引用类型为垃圾回收timeusagelifetimestrongreferenceisnever对象的一般状态terminatesoftreferenceJVMstoprunning内存不足objectcacheterminateweakreference内存不足Abort缓存垃圾回收后的幻象引用正常垃圾回收期间跟踪对象的垃圾回收垃圾回收后中止