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

问题描述 投票:756回答:11

java.lang.ref.WeakReferencejava.lang.ref.SoftReference有什么区别?

java reference weak-references soft-references
11个回答
882
投票

来自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
投票

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


0
投票

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

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

  1. -XX:SoftRefLRUPolicyMSPerMB = N标志(默认值为1000,又称1秒)
  2. 堆中的可用内存量。 例: 堆有10MB的可用空间(完全GC后); -XX:SoftRefLRUPolicyMSPerMB = 1000 如果上次访问的对象大于10秒,则将收集仅由SoftReference引用的对象。

198
投票

急切地收集弱引用。如果GC发现某个对象是弱可达的(只能通过弱引用访问),它将立即清除对该对象的弱引用。因此,它们很适合保存对程序也保留(强烈引用)“关联信息”的对象的引用,例如关于类的缓存反射信息,或对象的包装器等。任何使得在它与之关联的对象之后保持不变是GC-ed。当弱引用被清除时,它会在代码轮询某个地方的引用队列中排队,并且它也会丢弃关联的对象。也就是说,您保留有关对象的额外信息,但一旦它引用的对象消失,就不需要该信息。实际上,在某些情况下,您甚至可以继承WeakReference,并在WeakReference子类的字段中保留有关该对象的相关额外信息。 WeakReference的另一个典型用法是与Maps一起保存规范实例。

另一方面,SoftReferences适用于缓存外部可再生资源,因为GC通常会延迟清除它们。虽然在抛出OutOfMemoryError之前所有的SoftReferences都会被清除,但是理论上它们不会导致OOME [*]。

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

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

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


130
投票

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

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

Soft引用有资格由垃圾收集器收集,但在需要内存之前可能不会收集。即垃圾在OutOfMemoryError之前收集。

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

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

Source

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

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

72
投票

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

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

让我们看看下面的例子:我们有一个带有对象的Map,其中Key引用了一个对象。

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());

    }
}

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

WeakHashMap中

WeakHashMap是一个条目(key-to-value mappings)将被删除,当它不再可能从Map检索它们。

让我用WeakHashMap显示上面的例子

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());
    }
}

输出:使20 calls to System.gc()导致aMap size:0。

WeakHashMap只对键有弱引用,而不像其他Map类那样强引用。尽管您使用了WeakHashMap,但在强烈引用值或键时,您必须注意这些情况。通过将对象包装在WeakReference中可以避免这种情况。

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");
    }
}

软参考。

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

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


46
投票

软参考和弱参考之间唯一真正的区别是

垃圾收集器使用算法来决定是否回收可轻松访问的对象,但始终回收弱可达对象。


22
投票

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


8
投票

这个article可以非常有助于理解强,弱,弱和幻像参考。


为了给你一个总结,

如果您对对象只有弱引用(没有强引用),那么GC将在下一个GC循环中回收该对象。

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


所以你可以说,强引用具有终极能力(GC永远无法收集)

软引用比弱引用更强大(因为它们可以逃脱GC循环直到JVM耗尽内存)

弱引用甚至比软引用更不强大(因为它们不能逃避任何GC循环,并且如果对象没有其他强引用则会声明)。


餐厅比喻

  • 服务员 - GC
  • 你 - 堆中的对象
  • 餐厅区/空间 - 堆空间
  • 新客户 - 需要餐厅餐桌的新对象

现在,如果你是一个强大的客户(类似于强大的参考),那么即使一个新客户进入餐厅或者如此开心,你也永远不会离开你的桌子(堆上的内存区域)。服务员无权告诉您(甚至要求您)离开餐厅。

如果您是软客户(类似于软参考),那么如果新客户进入餐厅,服务员将不会要求您离开餐桌,除非没有其他空桌来容纳新客户。 (换句话说,只有当新客户介入并且没有其他表留给这位新客户时,服务员才会要求您离开桌子)

如果你是一个弱小的顾客(类似于弱参考),那么服务员可以随意(在任何时候)要求你离开餐厅:P


7
投票

The Only Real Difference

根据the doc,必须通过正在运行的GC清除松散的WeakReferences。

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

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

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


The only proper use of WeakReference is to observe the GC run. You do this by creating a new WeakReference whose object immediately goes out of scope, then try to get null out of weak_ref.get(). When it's null, you learn that between this duration, the GC ran.

至于错误使用WeakReference,列表是无穷无尽的:

  • 实现优先级为2的软引用的糟糕黑客,这样您就不必编写一个,但它不能按预期工作,因为即使有空闲内存,每次GC运行时都会清除缓存。请参阅https://stackoverflow.com/a/3243242/632951了解phails。 (此外,如果你需要超过2级的缓存优先级呢?你仍然需要一个真正的库。)
  • 将数据与现有类的对象关联的糟糕黑客,但是当您的GC决定在创建弱引用后休息时,它会创建内存泄漏(OutOfMemoryError)。此外,它超出了丑陋:更好的方法是使用元组。
  • 将数据与现有类的对象相关联的糟糕黑客,其中类具有使其自身不可子类化的神经,并且在您需要调用的现有函数代码中使用。在这种情况下,正确的解决方案是编辑类并使其成为子类,或编辑函数并使其采用接口而不是类,或使用替代函数。

3
投票

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

  1. 可以很容易访问的对象 - GC不会收集(回收占用的内存)这种对象。这些可以通过根节点或另一个强可达对象(即通过局部变量,类变量,实例变量等)到达。
  2. 可轻松访问的对象 - GC可能会尝试根据内存争用收集此类对象。这些可以通过一个或多个软参考对象从根目录访问
  3. 弱可达对象 - GC必须收集此类对象。这些可以通过一个或多个弱引用对象从根目录到达
  4. 可复活的对象 - GC已经在收集这些对象。但他们可能会通过执行一些终结器回到其中一个状态 - 强/弱/弱
  5. 虚拟可达对象 - GC已经在收集这些对象的过程中,并且已经决定不能通过任何终结器恢复(如果它本身声明了finalize()方法,那么它的终结器将被运行)。这些可以通过一个或多个幻像引用对象从根目录访问
  6. 无法访问的对象 - 对象既不强烈,柔和,弱,也不是幻像可达,并且不可复原。这些对象已准备好进行回收

更多细节:https://www.artima.com/insidejvm/ed2/gc16.html«崩溃

© www.soinside.com 2019 - 2024. All rights reserved.