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


Answers:


926

从Ethan Nicholas的《理解弱引用》中

参考不足

一个弱引用,简单地说,是不是强大到足以迫使对象保留在内存中的参考。弱引用使您可以利用垃圾收集器的能力来确定您的可达性,因此您不必自己做。您创建像这样的弱引用:

WeakReference weakWidget = new WeakReference(widget);

然后可以在代码中的其他地方使用 weakWidget.get()以获得实际 Widget对象。当然,弱引用不足以阻止垃圾回收,因此您可能会发现(如果没有对小部件的强引用) weakWidget.get()突然开始返回null

...

软参考

软参考酷似弱引用,不同之处在于它是更少急于扔掉其所引用的对象。只能弱访问的对象(对其的最强引用是WeakReferences)将在下一个垃圾回收周期中被丢弃,但是柔弱可达性的对象通常会停留一段时间。

SoftReferences并不需要表现与的任何不同 WeakReferences,但是实际上,只要有足够的内存,通常会保留可软访问的对象。这使它们成为缓存(例如上述图像缓存)的良好基础,因为您可以让垃圾回收器担心对象的可访问性(一个高度可访问的对象将永远不会从缓存中删除)。需要他们正在消耗的内存。

彼得·凯斯勒(Peter Kessler)在评论中添加:

Sun JRE确实将SoftReference与WeakReferences区别对待。如果可用内存没有压力,我们尝试保留由SoftReference引用的对象。一个细节:“-client”和“ -server” JRE的策略不同:-client JRE倾向于通过清除SoftReferences而不是扩展堆来减小占用的空间,而-server JRE尝试保持您的占用空间。通过优先扩展堆(如果可能)而不是清除SoftReferences,可以提高性能。一种尺寸并不适合所有尺寸。


7
发布更多消息,您可以在Wayback机器上找到它:web.archive.org/web/20061130103858/http
riccardo.tasso

这次,归档文件不再可用
-user1506104

209

急切地收集弱引用。如果GC发现某个对象是弱可访问的(仅可通过弱引用来访问),它将立即清除对该对象的弱引用。这样,它们对于保留对对象的引用很有用,您的程序也为该对象保留(强烈引用)某些“关联信息”,例如有关类的缓存反射信息或对象的包装等。在与对象关联的对象进行GC编辑后,没有任何保留意义。当弱引用被清除后,它将进入一个队列中,该队列将由您的代码在某个地方轮询,并且也丢弃关联的对象。也就是说,您保留了有关对象的额外信息,但是一旦该对象所指的对象消失了,则不需要该信息。其实,在某些情况下,您甚至可以继承WeakReference的子类,并将有关对象的相关额外信息保留在WeakReference子类的字段中。WeakReference的另一个典型用法是与Maps结合使用,以保留规范实例。

另一方面,SoftReferences非常适合缓存外部可重新创建的资源,因为GC通常会延迟清除它们。尽管可以保证在抛出OutOfMemoryError之前将清除所有SoftReferences,所以从理论上讲它们不会导致OOME [*]。

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

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

因此,决定取决于使用情况-如果要缓存构造成本很高但仍然可以从其他数据重构的信息,请使用软引用-如果要保留对某些数据的规范实例的引用,或者要在没有“拥有”对象的情况下拥有对对象的引用(因此可以防止将其进行GC处理),请使用弱引用。


14
对于何时使用弱对象的解释特别有用。
Jack BeNimble

正确使用a的一个关键点WeakReference是,在应该使用a的地方,可以容忍在引用超出范围后一小段时间仍然有效的事实,但这不是可取的。
2015年

我一直在努力理解WeakHashMap的用途,如果它总是对其键值对象产生弱引用?

@supercat,只有一种适当的用法WeakReference是观察GC运行。参见详细说明:stackoverflow.com/a/46291143/632951
Pacerier's

1
@Pacerier:该帖子的作者是完全错误的。他忽略了其他一些使用场景,例如事件订阅,他的第二点是荒谬的,而他的第三点则假定程序员可以做一些不可能的事情。他的第一点是合理的,但与我所说的直接相关。例如,如果代码经常不得不构建和比较大型的不可变对象,则如果代码创建新对象而不考虑它们是否已经存在,则构建部分的价格通常会便宜一些,但是将对象与自身(相同的引用)之间进行比较...
超级猫

155

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

强参考是一个正常的参考保护从收集通过GC称为对象。即永不垃圾收集。

一个软引用是符合回收的垃圾收集器,但可能不会被收集到需要它的内存中,直到。即垃圾收集之前OutOfMemoryError

一个弱引用是不被GC保护引用的对象从集合的引用。也就是说,当没有强引用或软引用时,垃圾将收集。

虚引用是它已经敲定后,一个对象的引用phantomly引用,但其分配的内存已经被回收之前。

资源

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

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

7
软引用... until memory is available没有意义。你是说is eligible for collection by garbage collector, but probably won't be collected until its memory is needed for another use
制造商史蒂夫(Steve),2015年

1
是的,直到内存可用,垃圾收集器才会收集引用。
2015年

2
我喜欢简单的解释性内容,没有太多bla bla +1!
阿德林

3
出色的总结和创新的范例
Ravindra babu


77

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

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

让我们看下面的示例:我们有一个Mapwith Objects,其中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


5
您可能会NullPointerException参加aMap.get().put(...)
xehpuk

您的第一个HashMap示例看起来不对。当您执行“ aMap.put(emp,val);”时 “ emp”和“ val”都是强引用。在内部,将创建一个新变量来保存'emp'和'val',因此当您执行“ emp = null;”时,您只是使“ emp”变量无效,而不是使哈希映射内部的变量无效(该变量仍保留原始的Employee对象)。因此,无论您对外部的'emp'变量进行何种操作,哈希映射仍将强烈引用'emp'。
Tiago

@Tiago。否。大概是通过“第一个示例”指的是WeakHashMap示例(因为这是第一个演示弱行为的示例)。看文档的“WeakHashMap中”:"An entry in a WeakHashMap will automatically be removed when its key is no longer in ordinary use. " 使用WeakHashMap中的整个的一点是,不必申报/传递在WeakReference的; WeakHashMap在内部为您完成此操作。docs.oracle.com/javase/7/docs/api/java/util/WeakHashMap.html
ToolmakerSteve

对System.gc()进行0调用,导致weakHashMap的大小为:0是第二个程序的输出吗?
阿德林

1
对于实际的另一个示例WeakHashMap,示例应用程序显示了如何仅在之后删除条目执行垃圾收集,请参阅对问题的回答:WeakHashMap是否在不断增长,还是清除了垃圾键?
罗勒·布尔克


25

SoftReference设计用于缓存。如果发现WeakReference引用引用了其他无法访问的对象,则它将立即被清除。SoftReference可能保持原样。通常,有一些算法与空闲内存量以及最后一次确定是否应清除内存的时间有关。当前的Sun算法将清除引用,前提是Java堆上没有可用的内存的秒数内没有使用该引用(可配置的服务器HotSpot将根据设置的最大可能堆进行检查-Xmx)。除非另外可以达到,否则SoftReference将在OutOfMemoryError抛出之前将s清除。



4
@DoctororDrive tbf问题是关于java的,不是dalvik!:-P
fabspro

2
@YaroslavMytkalyk,坦率地说,如果Android要重写类的行为,则应使用自己的名称空间,而不是java.lang。这样的同义词滥用无济于事。
Pacerier's

9

文章可以超有助于了解强,软,弱,虚引用。


为了给您一个总结,

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

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


所以你可以说,强大的参考具有最终的力量(GC永远无法收集)

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

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


餐厅类比

  • 服务员-GC
  • 您-堆中的对象
  • 餐厅面积/空间-堆空间
  • 新客户-在餐厅要桌子的新对象

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

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

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


7

唯一真正的不同

根据文档必须通过运行中的GC清除松散的WeakReferences 。

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

那是唯一的真正区别。其他所有内容都不是合同的一部分。(我认为最新文档是合同文件。)

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


WeakReference 的唯一正确用法是观察GC运行。为此,您可以创建一个新的WeakReference,该对象的对象立即超出范围,然后尝试从中获取null weak_ref.get()。为时null,您会了解到在此期间,GC已运行。

至于对WeakReference的错误使用,列表是无止境的:

  • 一个糟糕的hack,实现priority-2软引用,这样您就不必写一个,但是它不能按预期工作,因为即使在有备用内存的情况下,每次 GC运行都会清除缓存。请参阅 https://stackoverflow.com/a/3243242/632951以获取更多信息。(此外,如果您需要两个以上级别的缓存优先级,那该怎么办?您仍然需要一个真正的库。)

  • 将数据与现有类的对象相关联的糟糕方法,但是当您的GC决定在创建弱引用之后决定休息时,它会造成内存泄漏(OutOfMemoryError)。此外,这并不难看:更好的方法是使用元组。

  • 一种糟糕的hack,用于将数据与现有类的对象相关联,其中该类具有使自己无法子类化的神经,并且用在您需要调用的现有功能代码中。在这种情况下,正确的解决方案是编辑类并使它可成为子类,或者编辑函数并使它采用接口而不是类,或者使用替代函数。


密钥类型equals()只是对象身份的缓存怎么样?软引用在这里似乎是一种浪费,因为一旦不再牢固地达到关键对象,就不会再有人查找该映射了。
HighCommander4

我不同意。当您无论如何都不想影响GC时,请使用WeakReference(您可能希望保存一个对象引用,然后稍后检查它是否仍然存在,而没有任何偏好)。如果要影响GC尝试保留对象(例如,当您希望GC保留对象时),请使用SoftReference。
David Refaeli,

在Android的AsyncTask中使用WeakReference的一个很好的例子-保留上下文的实例。这样,如果上下文消失了(如果活动-屏幕旋转等),则AsyncTask将对其没有任何强引用,因此可以对其进行垃圾回收。检查youtu.be/...
大卫Refaeli

3

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

  1. 高度可访问的对象-GC 将不会收集(回收)此类对象。这些可以通过根节点或另一个高度可访问的对象 (即,通过局部变量,类变量,实例变量等)来访问。
  2. LY可达对象- GC 可以尝试收集这类对象的大小取决于内存争。可以通过一个或多个软参考对象从根目录访问这些对象
  3. LY可达对象- GC 必须收集这些类型的对象。这些可以通过一个或多个弱参考对象从根部到达
  4. 可复活的对象-GC已经在收集这些对象。但是通过执行某些终结器,它们可能会回到状态之一-强/弱/弱
  5. 幻影可到达的对象-GC 已在收集这些对象,并且已确定不能由任何终结器恢复(如果它本身声明了finalize()方法,则其终结器将已运行)。可以通过一个或多个幻像引用对象从根目录访问这些对象
  6. 无法访问的对象-对象既不能强,不能,不能弱,也不能幻像可访问,并且不能复活。这些对象已准备好回收

有关更多详细信息:https : //www.artima.com/insidejvm/ed2/gc16.html «合拢


4
对幻像引用的描述不是很好。另外,您已经按照特殊的顺序列出了4种类型。“幻像”是最弱的类型,而不是最强的类型。列出这些的传统顺序是“强,柔,弱,幻像”。而且我不知道您在哪里想到幻影对象用于缓存机制的想法。AFAIK,它们是仅由GC看到的临时状态,不是普通程序员可以使用的状态。
制造商

@ToolmakerSteve和所有-对几件事表示歉意1.我的回答的先前版本中对幻影引用的错误解释,以及2.延迟更正错误。现在,通过纠正错误,答案得到了改善
V.Vidyasagar

1

应该意识到,弱引用的对象只有在只有弱引用的情况下才会被收集。如果它具有多达一个强引用,则无论它有多少个弱引用都不会被收集。


这是常识... softref和phantomref也是如此。
Pacerier

1

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

重负载下的堆和GC行为

  • 强引用/强引用 -随着程序的继续,JVM无法收集保留的强引用对象。最终以“ java.lang.OutOfMemoryError:Java堆空间”结尾
  • 软参考 -随着程序的继续,堆使用率一直在增长,但是OLD gen GC在接近最大堆时发生了。在启动程序后,GC会及时启动。
  • 弱引用 -程序启动时,对象开始完成并几乎立即被收集。通常,对象是在年轻一代的垃圾收集中收集的。
  • 幻像引用 -与弱引用类似,幻像引用的对象也开始被最终确定并立即收集垃圾。没有旧的GC,所有对象都在年轻的垃圾收集本身中收集。

您可以在此处获得有关该实验的更多深度图,统计数据和观察结果


0

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

SoftReference:何时收集仅被软引用的对象取决于:

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

  2. 堆中的可用内存量。

    例:

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

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

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.