ChatGPT解决这个技术问题 Extra ChatGPT

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

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

SoftReferences 是 WeakReferences 的类型(不是真的,而是为了讨论),通常在 JVM 认为内存不足时收集。
@AjeetGanga,每当 GC 运行时,总是收集松散的弱引用。请参阅stackoverflow.com/a/46291143/632951

i
itsmysterybox

来自 Understanding Weak References,作者:Ethan Nicholas:

弱引用 简单地说,弱引用是一种强度不足以迫使对象保留在内存中的引用。弱引用允许您利用垃圾收集器为您确定可达性的能力,因此您不必自己做。你可以像这样创建一个弱引用: WeakReference weakWidget = new WeakReference(widget);然后在代码的其他地方,您可以使用 weakWidget.get() 来获取实际的 Widget 对象。当然,弱引用的强度不足以阻止垃圾回收,因此您可能会发现(如果没有对小部件的强引用)weakWidget.get() 突然开始返回 null。 ... 软引用 软引用与弱引用完全一样,只是它不太急于丢弃它所引用的对象。只能弱可达的对象(对它的最强引用是 WeakReferences)将在下一个垃圾回收周期被丢弃,但软可达的对象通常会保留一段时间。 SoftReferences 的行为不需要与 WeakReferences 有任何不同,但在实践中,只要内存充足,通常会保留软可访问对象。这使它们成为缓存(例如上面描述的图像缓存)的绝佳基础,因为您可以让垃圾收集器担心对象的可达性(强可达性对象永远不会从缓存中删除)以及它的严重程度需要他们正在消耗的内存。

彼得凯斯勒在评论中补充道:

Sun JRE 对 SoftReference 的处理与 WeakReference 不同。如果可用内存没有压力,我们会尝试保留由 SoftReference 引用的对象。一个细节:“-client”和“-server”JRE 的策略是不同的:-client JRE 试图通过更喜欢清除 SoftReferences 而不是扩展堆来保持你的占用空间小,而 -server JRE 试图保持你的通过更喜欢扩展堆(如果可能)而不是清除 SoftReferences 来提高性能。一种尺寸并不适合所有人。


没有更多可用的帖子,您可以在 Wayback 机器上找到它:web.archive.org/web/20061130103858/http://weblogs.java.net/blog/…
这一次,存档不再可用
很好的解释。这个问题可以出现在与 Java 相关的工作面试中。
d
driekken

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

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

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

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

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


对于解释何时使用弱对象特别有用。
关于正确使用 WeakReference 的一个关键点是,在应该使用它的地方,在引用超出范围后可能会在一段时间内保持有效这一事实可能是可以容忍的,但不是可取的。
如果 WeakHashMap 总是对其键值对象产生弱引用,我很难理解它的用途是什么?
@Pacerier:那篇文章的作者完全错了。他忽略了一些其他的使用场景,比如事件订阅,他的第二点是无意义的,他的第三点假设程序员可以做一些不可能的事情。他的第一点是有道理的,但与我所说的直接相关。例如,如果代码经常需要构建和比较大型不可变对象,如果代码创建新对象而不考虑它们是否已经存在,那么构建部分通常会更便宜,但是对象和自身之间的比较(相同的引用)将是...
...比两个拥有相同数据的不同不可变对象之间的比较便宜得多。如果代码需要一个与现有对象匹配的对象,那么如果这些对象最终会被比较,那么使用对现有对象的引用(而不是新对象)将很有帮助。但是,如果对象存在于内部缓存中并且没有其他地方存在,则返回对该对象的引用将比创建新对象并返回对该对象的引用更昂贵,并且没有任何优势。
P
Premraj

在Java中;从强到弱依次为:强、软、弱、幻

强引用是保护被引用对象不被 GC 收集的普通引用。即从不垃圾收集。

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

弱引用是不保护被引用对象不被 GC 收集的引用。即当没有强或软引用时垃圾收集。

幻影引用是对对象的引用,它在最终确定之后但在其分配的内存被回收之前被幻影引用。

Source

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

当国王强大时,GC无法杀死他。

当 King 变软时,GC 攻击他,但 King 以保护方式统治王国,直到资源可用。

当国王很弱时,GC 攻击他,但在没有保护的情况下统治王国。

当国王是幻影时,GC已经杀死了他,但国王可以通过他的灵魂获得。


软引用... 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吗?
是的,垃圾收集器在内存可用之前不会收集引用。
我喜欢简单解释的东西,没有太多的 bla bla bla +1 !
优秀的总结和创新的例子
+1,进一步阅读:javarevisited.blogspot.in/2014/03/…
P
Premraj

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

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

让我们看下面的示例:我们有一个带有 Objects 的 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 是在无法从 Map 中检索条目 (key-to-value mappings) 时将其删除的条目。

让我用 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 之前清除所有软引用。


您可能会在 aMap.get().put(...) 中获得 NullPointerException
您的第一个 HashMap 示例看起来不对。当您执行“aMap.put(emp, val);”时'emp' 和 'val' 都是强引用。在内部,会创建一个新变量来保存“emp”和“val”,因此当您执行“emp = null;”时您只是在使“emp”变量无效,而不是哈希映射内部的变量(它仍保留原始 Employee 对象)。因此,无论您对外部的 'emp' 变量做什么,散列映射仍将保留对 'emp' 的强引用。
@蒂亚戈。不。大概通过“第一个示例”,您指的是 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
对 System.gc() 进行了 0 次调用,导致weakHashMap 大小为:0 是您的第二个程序的输出吗?
有关 WeakHashMap 的另一个实际应用示例,示例应用程序展示了如何仅在执行垃圾收集 之后删除条目,请参阅问题的 my AnswerIs WeakHashMap ever-growing, or does it clear out the garbage keys?
i
itsmysterybox

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

垃圾收集器使用算法来决定是否回收一个软可达对象,但总是回收一个弱可达对象。


@ATorras,萨米尔。我在这里扩展了这个答案:stackoverflow.com/a/46291143/632951
T
Tom Hawtin - tackline

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


但在 Android 中,不建议缓存 developer.android.com/reference/java/lang/ref/…
@DoctororDrive tbf 问题是关于java,而不是dalvik! :-P
@YaroslavMytkalyk,坦率地说,如果 Android 想要重写一个类的行为,它应该使用自己的命名空间,而不是 java.lang。这种滥用同义词对任何人都没有好处。
L
Lavish Kothari

article 对理解强引用、软引用、弱引用和虚引用非常有帮助。

给大家总结一下,

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

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

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

软引用比弱引用更强大(因为它们可以逃避 GC 循环,直到 JVM 内存不足)

弱引用甚至不如软引用强大(因为它们无法摆脱任何 GC 循环,并且如果对象没有其他强引用,它们将被回收)。

餐厅类比

服务员 - GC

你 - 堆中的对象

餐厅区域/空间 - 堆空间

新客户 - 想要在餐厅用餐的新对象

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

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

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


A
Arsen Khachaturyan

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

强可达对象 - GC 不会收集(回收占用的内存)这种对象。这些可以通过根节点或另一个强可达对象(即通过局部变量、类变量、实例变量等)访问。软可达对象- GC 可能会根据内存争用尝试收集此类对象。这些可以通过一个或多个软引用对象从根中访问 弱可达对象 - GC 必须收集这种对象。这些可以通过一个或多个弱引用对象从根中访问 Resurrect-able objects - GC 已经在收集这些对象的过程中。但是它们可能会回到其中一种状态 - 通过执行某些终结器来实现强/软/弱 幻影可达对象 - GC 已经在收集这些对象的过程中,并且已经确定任何终结器都不能复活(如果它自己声明了一个 finalize() 方法,然后它的终结器将被运行)。这些可以通过一个或多个幻像引用对象从根中访问 不可访问对象 - 一个对象既不是强、软、弱,也不是幻像可访问的,并且不可恢复。这些对象已准备好进行回收

更多详情:https://www.artima.com/insidejvm/ed2/gc16.html « 折叠


对幻像引用的描述不是很好。此外,您以特殊的顺序列出了 4 种类型。 “幻影”是最弱的类型,不是最强的类型。列出这些的传统顺序是“强、软、弱、幻”。而且我不知道您从哪里得到幻像对象用于缓存机制的概念。 AFAIK,它们是只有 GC 才能看到的临时状态,而不是普通程序员可以使用的东西。
@ToolmakerSteve 和所有 - 为几件事道歉 1. 在我的答案的先前版本中对 Phantom 引用的错误解释,以及 2. 纠正错误的延迟。现在答案已通过更正错误得到改进
P
Pacerier

唯一真正的区别

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

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

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

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

恰当的

weak_ref.get()

无效的

至于 WeakReference 的错误使用,不胜枚举:

一个糟糕的 hack 来实现优先级 2 软引用,这样您就不必编写一个,但它不能按预期工作,因为缓存会在每次 GC 运行时被清除,即使有空闲内存也是如此。有关 phails,请参阅 https://stackoverflow.com/a/3243242/632951。 (此外,如果您需要 2 级以上的缓存优先级怎么办?您仍然需要一个真正的库。)

将数据与现有类的对象相关联的糟糕技巧,但是当您的 GC 决定在创建弱引用后休息时,它会产生内存泄漏 (OutOfMemoryError)。此外,它非常丑陋:更好的方法是使用元组。

将数据与现有类的对象相关联的糟糕技巧,其中该类有胆量使其自身不可子类化,并用于您需要调用的现有函数代码中。在这种情况下,正确的解决方案是编辑类并使其可子类化,或编辑函数并使其采用接口而不是类,或使用替代函数。


键类型的 equals() 只是对象标识的缓存呢?软引用在那里似乎是一种浪费,因为一旦一个关键对象不再是强可达的,没有人会再次查找该映射。
我不同意。当您不想以任何方式影响 GC 时使用 WeakReference(您可能希望保存一个对象引用,然后稍后检查它是否仍然存在,而没有任何偏好)。如果您想影响 GC 以尝试保留对象(即,当您希望 GC 保留它时),请使用 SoftReference。
使用 WeakReference 的一个很好的例子是在 Android 的 AsyncTask 中 - 保留上下文的实例。这样,如果上下文消失(如果活动 - 屏幕旋转等),AsyncTask 将不会对它有任何强引用,因此它可以被垃圾收集。检查youtu.be/…
R
Ravi K

为了给出实际的内存使用方面,我做了一个实验,在重负载下使用强、软、弱和幻影引用,将它们保留到程序结束。然后监控堆使用情况和 GC 行为。这些指标可能会因具体情况而异,但肯定会提供高水平的理解。以下是调查结果。

重负载下的堆和 GC 行为

强/硬引用 - 随着程序的继续,JVM 无法收集保留的强引用对象。最终以“java.lang.OutOfMemoryError: Java heap space”告终

软引用 - 随着程序的继续,堆使用量不断增长,但 OLD gen GC 在接近最大堆时发生。启动程序后,GC 启动时间稍晚。

弱参考 - 随着程序的启动,对象几乎立即开始完成并被收集。大多数对象在年轻代垃圾收集中被收集。

幻影引用 - 与弱引用类似,幻影引用的对象也开始完成并立即进行垃圾收集。没有老年代GC,所有对象都在年轻代垃圾收集本身中被收集。

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


F
Fai Lau

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


这是常识...... softref 和 phantomref 也是如此。
A
Artem Petrov

WeakReference:在每个 GC 周期(minor 或 full)收集仅被弱引用的对象。

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

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