我们之前提到过GC,但是当Java中引用的对象越来越多时,就会导致内存空间不足,最终会产生OutOfMemoryError错误,应用程序就会终止。那么为什么此时GC不能回收更多的对象呢?这和今天说的引用类型有关。首先,从JDK1.2开始,对象引用分为四个层次,让程序可以更灵活地控制对象的生命周期。这4个等级从高到低分别是:强参照、软参照、弱参照和虚参照。StrongReference强引用(StrongReference)是最常用的引用。如果一个对象有强引用,它永远不会被GC。例如:ObjectstrongReference=newObject();当内存空间不足时,JVM宁可抛出OutOfMemoryError导致程序异常终止,也不会随意回收强引用对象来解决内存不足的问题。如果没有使用到强引用对象,则需要对其进行弱化,使其能够被GC,比如ArrayList中的clear()方法:/***Removesalloftheelementsfromthislist.Thelistwill*beemptyafterthiscallreturns.*/publicvoidclear(){modCount++;//cleartoletGCdoitsworkfor(inti=0;isoftReference=newSoftReference<>(str);Stringresult=softReference.get();我们看一下get():publicTget(){To=super.get();//timestamp表示最后一次使用软引用的时间(初始化,get())//clock表示的时间lastGCif(o!=null&&this.timestamp!=clock)this.timestamp=clock;returno;}所以软引用在被垃圾回收时,也是遵循LRU规则,优先回收最近最少使用的对象进行回收.软引用的使用场景大多是内存敏感的缓存。具体来说,我们希望将数据存储在缓存中,以便可以快速读取。但是,当JVM中的内存不够用的时候,我们又不希望缓存的数据占用JVM的内存。例如,对于ReferenceQueue,如果软引用所引用的对象被垃圾回收,则JVM会将软引用添加到与其关联的引用队列中:ReferenceQueuereferenceQueue=newReferenceQueue<>();Stringstr=newString("abc");SoftReferencesoftReference=newSoftReference<>(str,referenceQueue);str=null;//NotifyGCSystem.gc();System.out.println(softReference.get());//abcReferencereference=referenceQueue.poll();System.out.println(reference);//null但是需要注意的是,如果使用软引用缓存,可能会导致FullGC的增加。弱引用如果一个对象只有弱引用(WeakReference),那么它的生命周期比软引用要短。在垃圾回收线程扫描其管辖内存区域的过程中,一旦发现只有弱引用的对象,无论当前内存空间是否充足,都会将其回收。然而,由于垃圾收集器是一个非常低优先级的线程,只有弱引用的对象可能无法快速找到。它的使用是:Stringstr=newString("abc");WeakReferenceweakReference=newWeakReference<>(str);str=weakReference.get();说到弱引用,就不得不提WeakHashMap。与HashMap相比,当我们为JVM分配的内存不足时,HashMap宁愿抛出OutOfMemoryError异常也不会回收其对应的未被引用的对象,而WeakHashMap会回收存储在其中但被引用的对象。WeakHashMap将一些未引用的键的值赋值给null,这样它会告诉GC回收这些存储的值。如果我们专门传入一个key为null的key,WeakHashMap会将key设置为一个特殊的Oject,源码为:publicVput(Kkey,Vvalue){//key会被重新赋值Objectk=maskNull(key);inth=hash(k);Entry[]tab=getTable();inti=indexFor(h,tab.length);for(Entrye=tab[i];e!=null;ee=e.next){if(h==e.hash&&eq(k,e.get())){VoldValue=e.value;if(value!=oldValue)e.value=value;returnoldValue;}}modCount++;Entrye=tab[i];tab[i]=newEntry<>(k,value,queue,h,e);if(++size>=threshold)resize(tab.length*2);returnnull;}/***表示表内空键的值。*特殊键*/privatestaticfinalObjectNULL_KEY=newObject();/***UseNULL_KEYforkeyifitisnull.*/privatestaticObjectmaskNull(Objectkey){return(key==null)?NULL_KEY:key;}幻引用(PhantomReference),顾名思义,是没有用的。与其他类型的引用不同,虚引用不决定对象的生命周期。如果一个对象只持有虚引用,就好像它没有引用一样,随时可能被垃圾收集器回收。幻象引用主要用于跟踪被垃圾收集器回收的对象的活动。幻影引用与软引用和弱引用的区别之一是幻影引用必须与引用队列(ReferenceQueue)结合使用。当垃圾回收器要回收一个对象时,如果发现它还有一个虚引用,就会把这个虚引用添加到与之关联的引用队列中,然后再回收该对象的内存。例如:Stringstr=newString("abc");ReferenceQueuequeue=newReferenceQueue();//创建一个幻象引用,需要关联一个引用队列PhantomReferencepr=newPhantomReference(str,queue);程序可以判断引用队列中是否加入了Phantom引用,从而知道被引用的对象是否即将被垃圾回收。如果程序发现引用队列中加入了虚引用,可以在被引用对象的内存被回收前采取必要的动作,也可以理解为回调方法。通过表格从高到低总结Java中四种引用的等级和强度:强引用->软引用->弱引用->幻象引用来说明其特点: