Java中的SoftReference和WeakReference有什么区别?

时间:2008-11-18 18:26:38

标签: java reference weak-references soft-references

12 个答案:

答案 0 :(得分:870)

来自Understanding Weak References,来自Ethan Nicholas:

  

弱引用

     

简单地说,弱引用是一个   参考不够强大   强制对象留在内存中。   弱引用允许您利用   垃圾收集器的能力   确定你的可达性,所以你   不必自己动手。您   创建一个像这样的弱引用:

WeakReference weakWidget = new WeakReference(widget);
     

然后   你可以使用的代码中的其他地方   weakWidget.get()得到实际的   Widget对象。当然弱者   参考不够强大   防止垃圾收集,所以你可以   找(如果没有强者   对小部件的引用)   weakWidget.get()突然开始了   返回null

     

...

     

软参考

     

软参考就像一个   弱参考,除了它更少   渴望扔掉对象   它指的是什么。一个对象是   只有微弱的可达性(最强的   对它的引用是WeakReferences)   将在下一个垃圾丢弃   收集周期,但一个对象   通常会轻柔地到达   坚持了一会儿。

     

SoftReferences不是必需   表现得与...不同   WeakReferences,但实际上是轻声细语   一般可达到的对象   只要内存在,就会保留   供应充足。这使他们成为了一个   这样的缓存的良好基础   作为上述图像缓存,   既然你可以让垃圾   收藏家担心如何   物体是可到达的(强烈的   可访问的对象永远将被删除   来自缓存)以及它需要多么糟糕   他们正在消耗的记忆。

Peter Kessler在评论中补充道:

  

Sun JRE确实以不同于WeakReferences的方式处理SoftReferences。如果可用内存没有压力,我们会尝试保持SoftReference引用的对象。一个细节:“-client”和“-server”JRE的策略是不同的:-client JRE试图通过清除SoftReferences而不是扩展堆来保持您的足迹小,而-server JRE尝试保留您的通过优先扩展堆(如果可能)而不是清除SoftReferences来提高性能。一种尺寸并不适合所有人。

答案 1 :(得分:195)

急切地收集弱引用。如果GC发现对象是 弱可达(只能通过弱参考),它会清除 立即对该对象的弱引用。因此,他们是有益的 保持对程序也保留的对象的引用 (强烈引用)“关联信息”有些像缓存一样 关于类的反射信息,或对象的包装器等。 任何与它关联的对象之后没有意义的东西 与GC编辑。当弱引用被清除时,它会被排入队列中 您的代码在某处轮询的引用队列,它丢弃了 相关对象也是如此。也就是说,您保留有关的额外信息 对象,但是一旦它引用的对象就不需要该信息 消失了。实际上,在某些情况下,你甚至可以继承 WeakReference并保留关于对象的相关额外信息 在WeakReference子类的字段中。另一种典型用途 WeakReference与Maps一起用于保存规范实例。

另一方面,SoftReferences有助于缓存外部可再生资源 因为GC通常会延迟清除它们。虽然这一切都得到保证 在引发OutOfMemoryError之前,SoftReferences将被清除,因此它们将被清除 理论上不能引起OOME [*]。

典型的用例示例是保存来自a的已解析形式的内容 文件。您将实现一个系统,您可以在其中加载文件,解析并保留文件 SoftReference到解析表示的根对象。下次 您需要该文件,您将尝试通过SoftReference检索它。如果 你可以检索它,你自己另外加载/解析,如果GC 在此期间清除它,你重新加载它。这样,你可以免费使用 内存用于性能优化,但不要冒OOME风险。

现在为[*]。保持SoftReference本身不会导致OOME。如果 另一方面,您错误地将SoftReference用于WeakReference的任务 使用(即,您以某种方式保留与对象关联的信息 强烈引用,并在Reference对象获取时丢弃它 已清除),您可以作为轮询ReferenceQueue的代码进入OOME 并丢弃相关对象可能碰巧不及时运行 方式。

因此,决定取决于使用情况 - 如果您正在缓存构建成本高昂的信息,但是 尽管如此,可以从其他数据重建,使用软引用 - 如果您要保留对某些数据的规范实例的引用,或者 你想要一个对象的引用而不“拥有”它(因此 防止它被GC'd),使用弱引用。

答案 2 :(得分:124)

在Java中;从最强到最弱的顺序,有:强,软,弱和幻影

强引用是一种常规引用,用于保护引用的对象不受GC的收集。即从不垃圾收集。

软参考符合垃圾收集器的收集条件,但在需要内存之前可能无法收集。即垃圾在OutOfMemoryError之前收集。

弱引用是一种不保护GC引用的引用对象的引用。即没有强力或软质参考时垃圾收集。

幻像引用是对一个对象的引用,它在完成后被引用,但在它已分配的内存被回收之前。

Source

打个比方:假设JVM是一个王国,对象是王国的王者,而GC则是试图杀死国王(对象)的王国的攻击者。

  • 当King 时,GC无法杀死他。
  • 当King Soft 时,GC会攻击他,但King会保护王国,直到资源可用。
  • 当国王时,GC会攻击他,但在没有保护的情况下统治王国。
  • 当国王幻影时,GC已经杀死了他,但国王可以通过他的灵魂获得。

答案 3 :(得分:70)

弱参考 http://docs.oracle.com/javase/1.5.0/docs/api/java/lang/ref/WeakReference.html

原则: weak reference与垃圾回收有关。通常,具有一个或多个reference的对象将不符合垃圾回收的条件 当weak referenceMap时,上述原则不适用。如果一个对象只有其他对象的弱引用,那么它就可以进行垃圾回收了。

让我们看看下面的例子:我们有一个import java.util.HashMap; public class Test { public static void main(String args[]) { HashMap<Employee, EmployeeVal> aMap = new HashMap<Employee, EmployeeVal>(); Employee emp = new Employee("Vinoth"); EmployeeVal val = new EmployeeVal("Programmer"); aMap.put(emp, val); emp = null; System.gc(); System.out.println("Size of Map" + aMap.size()); } } 对象,其中Key引用一个对象。

emp  = null

现在,在执行程序期间,我们已经Map。拿着钥匙的null在这里毫无意义,因为它是WeakHashMap。在上述情况下,对象不是垃圾回收。

<强> WeakHashMap中

key-to-value mappings是指在Map无法再检索条目时将删除条目import java.util.WeakHashMap; public class Test { public static void main(String args[]) { WeakHashMap<Employee, EmployeeVal> aMap = new WeakHashMap<Employee, EmployeeVal>(); Employee emp = new Employee("Vinoth"); EmployeeVal val = new EmployeeVal("Programmer"); aMap.put(emp, val); emp = null; System.gc(); int count = 0; while (0 != aMap.size()) { ++count; System.gc(); } System.out.println("Took " + count + " calls to System.gc() to result in weakHashMap size of : " + aMap.size()); } } )。

让我用 WeakHashMap

显示上面的例子
20 calls to System.gc()

输出:取aMap size导致WeakHashMap:0。

Map只有对密钥的弱引用,而不像其他WeakHashMap类那样强引用。尽管您已使用import java.lang.ref.WeakReference; import java.util.HashMap; public class Test { public static void main(String args[]) { HashMap<Employee, EmployeeVal> map = new HashMap<Employee, EmployeeVal>(); WeakReference<HashMap<Employee, EmployeeVal>> aMap = new WeakReference<HashMap<Employee, EmployeeVal>>( map); map = null; while (null != aMap.get()) { aMap.get().put(new Employee("Vinoth"), new EmployeeVal("Programmer")); System.out.println("Size of aMap " + aMap.get().size()); System.gc(); } System.out.println("Its garbage collected"); } } ,但在强烈引用值或键时,您必须注意这些情况。将对象包装在 WeakReference 中可以避免这种情况。

Soft Reference

软参考。

OutOfMemoryError比弱参考略强。软引用允许垃圾收集,但只有在没有其他选项时才请求垃圾收集器清除它。

垃圾收集器不会像弱处理对象那样积极地收集可轻松访问的对象 - 而是只收集可以轻松访问的对象(如果它真的“需要”内存)。软引用是向垃圾收集器说的一种方式,“只要内存不是太紧,我就想保留这个对象。但是如果内存变得非常紧张,请继续收集它并且我会处理它接着就,随即。”垃圾收集器需要清除所有软引用,然后才能抛出{{1}}。

答案 4 :(得分:46)

软引用和弱引用之间唯一真正的区别是

  

垃圾收集器使用算法来决定是否   回收一个柔和的可达物体,但总是微弱地回收   可到达的对象。

答案 5 :(得分:22)

SoftReference专为缓存而设计。当发现WeakReference引用其他无法访问的对象时,它将立即被清除。 SoftReference可能会保留原样。通常,存在一些与空闲存储量和最后用于确定是否应该被清除的时间有关的算法。当前的Sun算法是清除引用,如果它在数秒内没有被使用,那么Java堆上有可用内存的兆字节(可配置,服务器HotSpot检查-Xmx设置的最大可能堆)。 <{1}}将在SoftReference被抛出之前被清除,除非另有可达。

答案 6 :(得分:7)

唯一的真正差异

每个the doc,正在运行的GC会清除松散的WeakReferences

the doc,在抛出OOM之前,必须清除松散的SoftReferences

这是唯一真正的区别。其他一切都不是合同的一部分。 (我假设最新的文档是合同的。)

SoftReferences非常有用。内存敏感的缓存使用SoftReferences,而不是WeakReferences。

<小时/> WeakReference的唯一正确使用是观察GC运行。您可以通过创建一个新的WeakReference来完成此操作,该WeakReference的对象会立即超出范围,然后尝试从weak_ref.get()中获取null。当它为null时,您会了解到,在此期间,GC会运行。

至于不正确使用WeakReference,列表是无穷无尽的:

  • 实现优先级为2的软引用的糟糕黑客,这样你就不必写一个,它不能按预期工作,因为缓存将被清除每次 GC运行,即使有空闲内存。有关phails的信息,请参阅https://stackoverflow.com/a/3243242/632951。 (此外,如果你需要超过2级的缓存优先级呢?你仍然需要一个真正的库。)

  • 将数据与现有类的对象关联起来的糟糕黑客然后当GC在您的弱引用创建后决定休息时会产生内存泄漏(OutOfMemoryError)。此外,它超出了丑陋:更好的方法是使用元组。

  • 糟糕的黑客将数据与现有类的对象相关联,其中类具有使其自身不可子类化的神经,并且用于现有功能代码中需要打电话。在这种情况下,正确的解决方案是编辑类并使其成为子类,或者编辑函数并使其成为接口而不是类,或者使用替代函数。

答案 7 :(得分:7)

article对于理解强,弱,弱和幻像引用非常有帮助。


为您提供一个摘要,

如果您仅对对象弱引用(没有强引用),则该对象将在下一个GC周期被GC回收。

如果对对象只有软引用(没有强引用),则只有当JVM内存不足时,GC才会回收该对象。


所以您可以说,强引用具有终极威力(无法被GC收集)

软引用比弱引用功能强大(因为它们可以逃避GC周期,直到JVM内存耗尽)

弱引用比软引用功能更弱(因为它们无法排除任何GC周期,如果对象没有其他强引用,则将其回收)。


餐厅类比

  • 侍者-GC
  • 您-堆中的对象
  • 餐厅区域/空间-堆空间
  • 新客户-想要在餐厅中使用桌子的新对象

现在,如果您是强大的客户(类似于强大的推荐人),那么即使有新客户进来餐厅或发生什么事情,您也永远不会离开桌子(存储区)在堆上)。服务员无权告诉您(甚至要求您)离开餐厅。

如果您是软顾客(类似于软参考),那么如果有新顾客进来,餐厅的服务员将不会要求您离开桌子,除非没有其他空桌子留下来容纳新客户。 (换句话说,服务员只会在有新客户进场并且没有其他可供该新客户使用的桌子时才要求您离开桌子)

如果您是弱顾客(类似于弱引用),那么服务员可以(在任何时间)要求您离开餐厅:P

答案 8 :(得分:3)

Java中六种类型的对象可达性状态 -

  1. 可达对象 - GC 不会收集(回收占用的内存)此类对象。这些可通过根节点或另一个强可达对象(即通过局部变量,类变量,实例变量等)到达。
  2. 可达对象 - GC 可能会尝试根据内存争用情况收集此类对象。这些可通过一个或多个软参考对象
  3. 从根目录访问
  4. 可到达的对象 - GC 必须收集此类对象。这些 可以通过一个或多个弱引用对象
  5. 从根目录访问
  6. Resurrect-able 对象 - GC已经在收集这些对象。但他们可能会通过执行一些终结者回到其中一个州 - 强/弱/弱
  7. 幻影可达对象 - GC 已经在收集这些对象的过程中,并且已决定不能通过任何终结器进行复活(如果它声明了finalize()方法本身,然后它的终结器将被运行)。这些可以通过一个或多个幻像参考对象
  8. 从根目录访问
  9. 无法访问对象 - 对象既不强烈,柔和,弱,也不是幻像可达,并且不可复原。这些对象已准备好进行回收
  10. 有关详情:https://www.artima.com/insidejvm/ed2/gc16.html«崩溃

答案 9 :(得分:1)

应该知道,弱引用对象只有在只有弱引用时才会被收集。如果它有一个强大的引用,那么无论它有多少弱引用它都不会被收集。

答案 10 :(得分:0)

WeakReference :每个GC周期(次要或完整)都会收集仅被弱引用的对象。

SoftReference :当只收集软引用的对象时,取决于:

  1. -XX:SoftRefLRUPolicyMSPerMB = N标志(默认值为1000,又称1秒)

  2. 堆中的可用内存量。

    示例:

    • 堆有10MB的可用空间(完全GC后);
    • -XX:SoftRefLRUPolicyMSPerMB = 1000

    如果上次访问的对象大于10秒,则将收集仅由SoftReference引用的对象。

答案 11 :(得分:0)

为了给出实际的内存使用情况,我对重载,重载,软载,虚弱和幻像引用进行了实验,将重载对象保留到程序结尾。然后监视堆使用情况和GC行为。这些指标可能会因情况而异,但可以肯定地提供了高水平的理解。以下是调查结果。

重负载下的堆和GC行为

  • 强引用/强引用-随着程序的继续,JVM无法收集保留的强引用对象。最终以“ java.lang.OutOfMemoryError:Java堆空间”结束
  • 软件参考-随着程序的继续,堆使用率不断增长,但是OLD gen GC在接近最大堆时发生了。在启动程序后,GC的启动时间稍晚一些。
  • 弱引用-程序启动时,对象开始完成并几乎立即被收集。大多数情况下,对象都是在年轻一代的垃圾收集中收集的。
  • 幻影引用-与弱引用相似,幻影引用的对象也开始被最终确定并立即收集垃圾。没有旧的GC,所有对象都被收集到了年轻的垃圾回收本身中。

您可以深入了解graphs, stats, observations for this experiment here