介绍 JAVA中有4种类型的引用: 强引用 软引用 弱引用 虚引用 这些引用仅在垃圾收集器管理它们的方式上有所不同。如果您从未听说过它们,则意味着您只使用过强的。了解其中的区别会对您有所帮助,尤其是当您需要存储临时对象并且不能使用像eHcache或Guava这样的真正的缓存库时。 由于这些类型与JVM垃圾收集器密切相关,因此我将简要回顾一些有关JAVA中垃圾收集的信息,然后介绍不同的类型。垃圾收集器 Java和C之间的主要区别是内存管理。在Java中,开发人员不需要知道内存是如何工作的(但他应该知道!),因为JVM使用其垃圾收集器来处理这部分。 当您创建一个对象时,它由JVM在其堆中分配。堆是内存中有限的空间。因此,JVM经常需要删除对象以释放空间。为了销毁一个对象,JVM需要知道这个对象是活跃的还是不活跃的。如果一个对象被垃圾收集根引用(传递地),则该对象仍在使用中。 例如:如果对象C被对象B引用,B被对象A引用,A被垃圾回收根引用,那么C、B和A被认为是活动的(情况1)。但是,如果B不再被A引用,则C和B不再处于活动状态并且可以被销毁(情况2)。 由于这篇文章不是关于垃圾收集器的,所以我不会深入解释,但仅供参考,有4种类型的垃圾收集器根:局部变量活跃的Java线程静态变量JNI引用是包含本机代码的Java对象,而不是由jvm管理的内存 Oracle没有指定如何管理内存,因此每个JVM实现都有自己的一套算法。但想法总是一样的: JVM使用循环算法寻找非活动对象并标记它们 标记的对象被终结(调用finalize()方法)然后被销毁 JVM有时会移动剩余对象的一部分为了重建堆中大面积的空闲连续空间问题 如果JVM管理内存,您为什么需要关心?因为这并不意味着你不能有内存泄漏! 大多数时候,您都在不知不觉中使用垃圾收集根。例如,假设您需要在程序的生命周期内存储一些对象(因为它们的初始化成本很高)。您可能会使用静态集合(List、Map等)在代码中的任何位置存储和检索这些对象:publicstaticMapK,VmyStoredObjectsnewHashMap(); 但是,这样做可以防止JVM破坏集合中的对象。您可能会错误地遇到OutOfMemoryError。例如:publicclassOOM{publicstaticListIntegermyCachedObjectsnewArrayList();publicstaticvoidmain(String〔〕args){for(inti0;i100000000;i){myCachedObjects。add(i);}}} 输出是: 线程main中的异常java。lang。OutOfMemoryError:Java堆空间 Java提供了不同类型的引用来避免OutOfMemoryError。 有些类型允许JVM释放对象,即使程序仍然需要它们。处理这些情况是开发人员的责任。强引用 强引用是标准引用。当您像这样在对象obj上创建时: MyClassobjnewMyClass(); 您正在为新创建的MyClass实例创建一个名为obj的强引用。当垃圾收集器查找非活动对象时,它只检查对象是否是强可达的,这意味着通过强引用可传递地链接到垃圾收集根。 使用这种类型的引用会强制JVM将对象保留在堆中,直到对象不被使用,如垃圾收集器部分所述。软引用 根据javaAPIsoftreference有: 软引用对象,由垃圾收集器根据内存需求自行清除 这意味着如果您在不同的JVM(Oracle的Hotspot、Oracle的JRockit、IBM的J9等)上运行您的程序,软引用的行为可能会发生变化。 让我们看一下Oracle的JVMHotspot(标准和最常用的JVM),看看它是如何管理软引用的。根据Oracle文档: 默认值为每兆字节1000毫秒,这意味着对于堆中每兆字节的可用空间,软引用将存活(在收集到对象的最后一个强引用之后)1秒 这是一个具体的例子:假设堆是512MB,还有400MB空闲。 我们创建一个对象A,软引用到对象缓存,并强引用A到对象B。由于A被强引用到B,它是强可达的并且不会被垃圾收集器删除(案例1)。 想象一下,现在B被删除了,所以A只是对缓存对象的软引用。如果对象A在接下来的400秒内没有被强引用,它将在超时后被删除(情况2)。 以下是如何操作软引用:publicclassExampleSoftRef{publicstaticclassA{}publicstaticclassB{privateAstrongRpublicvoidsetStrongRef(Aref){this。strongR}}publicstaticSoftRpublicstaticvoidmain(String〔〕args)throwsInterruptedException{initialisationofthecachewithasoftreferenceofinstanceAExampleSoftRef。AinstanceAnewExampleSoftRef。A();cachenewSoftReferenceExampleSoftRef。A(instanceA);instanceAinstanceAisnowonlysoftreachableandcanbedeletedbythegarbagecollectoraftersometimeThread。sleep(5000);。。。ExampleSoftRef。BinstanceBnewExampleSoftRef。B();sincecachehasaSoftReferenceofinstanceA,wecantbesurethatinstanceAstillexistsweneedtocheckandrecreateaninstanceAifneededinstanceAcache。get();if(instanceAnull){instanceAnewExampleSoftRef。A();cachenewSoftReferenceExampleSoftRef。A(instanceA);}instanceB。setStrongRef(instanceA);instanceAinstanceAaisnowonlysoftlyreferencedbycacheandstronglyreferencedbyBsoitcannotbeclearedbythegarbagecollector。。。}} 但是即使软引用对象被垃圾收集器自动删除,软引用(也是对象)也没有被删除!所以,你仍然需要清除它们。例如,对于像64Mbytes(Xmx64m)这样的小堆大小,尽管使用了软引用,但以下代码会给出OutOfMemoryException。publicclassTestSoftReference1{publicstaticclassMyBigObject{eachinstancehas128bytesofdataint〔〕datanewint〔128〕;}publicstaticintCACHEINITIALCAPACITY1000000;publicstaticSetSoftReferenceMyBigObjectcachenewHashSet(CACHEINITIALCAPACITY);publicstaticvoidmain(String〔〕args){for(inti0;i1000000;i){MyBigObjectobjnewMyBigObject();cache。add(newSoftReference(obj));if(i2000000){System。out。println(sizeofcache:cache。size());}}System。out。println(End);}} 输出代码是: 缓存大小:1 缓存大小:200001 缓存大小:400001 缓存大小:600001 线程主java。lang。OutOfMemoryError中的异常:超出GC开销限制 Oracle提供了一个ReferenceQueue,当引用的对象只能软访问时,它会填充软引用。使用此队列,您可以清除软引用并避免OutOfMemoryError。 使用ReferenceQueue,与上面相同的代码具有相同的堆大小(64MB)但要存储更多数据(500万对100万):publicclassTestSoftReference2{publicstaticintremovedSoftRefs0;publicstaticclassMyBigObject{eachinstancehas128bytesofdataint〔〕datanewint〔128〕;}publicstaticintCACHEINITIALCAPACITY1000000;publicstaticSetSoftReferenceMyBigObjectcachenewHashSet(CACHEINITIALCAPACITY);publicstaticReferenceQueueMyBigObjectunusedRefToDeletenewReferenceQueue();publicstaticvoidmain(String〔〕args){for(inti0;i5000000;i){MyBigObjectobjnewMyBigObject();cache。add(newSoftReference(obj,unusedRefToDelete));clearUselessReferences();}System。out。println(End,removedsoftreferencesremovedSoftRefs);}publicstaticvoidclearUselessReferences(){R?extendsMyBigObjectrefunusedRefToDelete。poll();while(ref!null){if(cache。remove(ref)){removedSoftR}refunusedRefToDelete。poll();}}} 输出是: 结束,删除软引用4976899 当您需要存储许多对象时,软引用很有用,如果它们被JVM删除,这些对象可能会(代价高昂)重新实例化。弱引用 弱引用是一个比软引用更易变的概念。根据JAVAAPI: 假设垃圾收集器在某个时间点确定一个对象是弱可达的。届时,它将自动清除对该对象的所有弱引用以及对该对象可通过强引用链和软引用链访问的任何其他弱可达对象的所有弱引用。同时,它会声明所有以前的弱可达对象是可终结的。同时或在稍后的某个时间,它会将那些在引用队列中注册的新清除的弱引用排入队列。 这意味着当垃圾收集器检查所有对象时,如果它检测到一个对象只有对垃圾收集根的弱引用(即没有强引用或软引用链接到该对象),该对象将被标记为移除并尽快删除。使用WeakReference的方法与使用SoftReference完全相同。因此,请看软引用部分的示例。 Oracle提供了一个非常有趣的基于弱引用的类:WeakHashMap。该映射具有弱引用键的特殊性。WeakHashMap可以用作标准Map。唯一的区别是它会在键从堆中销毁后自动清除:publicclassExampleWeakHashMap{publicstaticMapInteger,StringcachenewWeakHashMapInteger,String();publicstaticvoidmain(String〔〕args){Integeri5newInteger(5);cache。put(i5,five);i5theentry{5,five}willstayintheMapuntilthenextgarbagecollectorcallIntegeri22;theentry{2,two}willstayintheMapuntili2isnomorestronglyreferencedcache。put(i2,two);remebmbertheOutOfMemoryErroratthechapterproblem,thistimeitwonthappenbecausetheMapwillclearitsentries。for(inti6;i100000000;i){cache。put(i,String。valueOf(i));}}} 例如,我使用WeakHashMap来解决以下问题:存储多个交易信息。我使用了这个结构:WeakHashMapString,MapK,V其中WeakHashMap的键是一个包含交易ID的字符串,简单Map是我需要在生命周期内保留的信息交易。有了这个结构,我肯定会在WeakHashMap中获取我的信息,因为包含事务ID的字符串在事务结束之前不会被销毁,而且我不必关心清理Map。 Oracle建议使用WeakHashMap作为规范化映射。虚引用 在垃圾收集过程中,没有对垃圾收集根的强软引用的对象将被删除。在被删除之前,方法finalize()被调用。当一个对象被终结但没有被删除(还)时,它就变成了幻影可达,这意味着在对象和垃圾收集根之间只有一个幻影引用。 与软引用和弱引用不同,对对象使用显式幻像引用可以防止对象被删除。程序员需要显式或隐式地移除幻影引用,以便销毁最终化的对象。要显式清除幻影引用,程序员需要使用ReferenceQueue,当对象完成时,它会填充幻影引用。 幻影引用无法检索被引用的对象:幻影引用的get()方法始终返回null,因此程序员无法再次使幻影可达对象强软弱可达。这是有道理的,因为幻影可达对象已经完成,所以如果重写的finalize()函数已清除资源,它可能不再工作。 由于无法访问引用的对象,因此我看不出幻影引用有何用处。一个用例可能是,如果您需要在对象完成后执行操作,而您不能(或出于性能原因不想)在该对象的finalize()方法中执行特定操作。结论 我希望您现在对这些参考资料有了更好的了解。大多数时候,您不需要显式使用它们(也不应该)。但是,许多框架正在使用它们。如果你想了解东西是如何工作的,那么了解这个概念是很好的。