在Java中,软参考和弱参考有什么区别?

241159 次浏览

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

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

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

现在是[*]。保留SoftReference本身不会导致OOME。如果另一方面,你错误地将Soft引用用于一个任务,一个弱引用的意思是要使用(即,您以某种方式保留与对象关联的信息强引用,并在Reference对象获取时丢弃它清除),你可以运行OOME作为你的代码,轮询引用队列并丢弃关联的对象可能碰巧没有及时运行时尚。

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

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

来自理解弱引用,伊桑·尼古拉斯:

弱引用

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

WeakReference weakWidget = new WeakReference(widget);

然后您可以使用的代码中的其他位置weakWidget.get()获取实际Widget对象。当然是弱者参考不足以防止垃圾回收机制,所以你可以发现(如果没有强对小部件的引用)weakWidget.get()突然启动返回null.

软参考

A软引用类似于弱参考,除了它更少急于扔掉物体它指的是。一个物体只能微弱地到达(最强的对它的引用是WeakReferences)将被丢弃在下一个垃圾桶收集循环,但一个对象一般都能轻柔地到达#36825;待一段时间

SoftReferences不是选填/必填到行为与WeakReferences,但在实践中可达对象通常是只要内存在就保留充足的供应。这使他们成为为缓存奠定了良好的基础,例如如上所述的图像缓存,因为你可以让垃圾收藏家担心如何可达的对象是(一个强可达对象将从未被删除从缓存)以及它有多需要他们正在消耗的内存。

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

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

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

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

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

原理:weak reference与垃圾回收机制有关。通常,具有一个或多个reference的对象不符合垃圾回收机制的条件。
上述原则在weak reference时不适用。如果一个对象对其他对象只有弱引用,那么它就准备好了垃圾回收机制。

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

import java.util.HashMap;public class Test {
public static void main(String args[]) {HashMap<Employee, EmployeeVal> aMap = newHashMap<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是当不再可能从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 of: 0。

WeakHashMap对键只有弱引用,而不是像其他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");}}

软参考。

Soft Reference比弱引用稍微强一点。软引用允许垃圾回收机制,但只有在没有其他选项时才请求垃圾回收器清除它。

垃圾收集器不会像收集弱可达对象那样积极地收集软可达对象——相反,它只在确实“需要”内存时收集软可达对象。软引用是对垃圾收集器说:“只要内存不太紧,我想保留这个对象。但如果内存真的很紧,请继续收集它,我会处理的。”垃圾收集器需要在抛出OutOfMemoryError之前清除所有软引用。

从最强到最弱的顺序是:强,软,弱和幻影

强大的参考是一个普通引用,它保护引用的对象不被GC收集。即从不垃圾收集。

软参考有资格被垃圾收集器收集,但可能在需要它的内存之前不会被收集。即垃圾收集在OutOfMemoryError之前。

弱参考是一个引用,它不保护被引用的对象不被GC收集。即在没有强引用或软引用时进行垃圾收集。

幻影参考是对一个对象的引用,在它被最终确定之后,但在它分配的内存被回收之前,它被虚幻地引用。

来源

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

  • 当国王是强大时,GC不能杀死他。
  • 当国王是时,GC攻击他,但国王统治王国,直到资源可用。
  • 当国王是时,GC攻击他,但在没有保护的情况下统治王国。
  • 当国王是Phantom时,GC已经杀死了他,但国王可以通过他的灵魂获得。

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

  1. 强大 ly可达对象-GC不会收集(回收被占用的内存)这种对象。这些是可通过根节点或其他强可达对象访问(即通过局部变量、类变量、实例变量等)
  2. ly可达对象-GC可能试图根据内存争用收集这种对象。这些可以通过一个或多个软引用对象从根访问
  3. ly可达对象-GC必须收集这种对象。这些可通过一个或多个弱引用对象
  4. 从根访问
  5. 可复活的对象-GC已经在收集这些对象的过程中。但是他们可能会回到其中一个状态-强/软/弱通过执行一些终结器
  6. Phantomly可达对象-GC已经在收集这些对象的过程中,并且已经确定不能被任何终结器复活(如果它声明了一个finaliz()方法本身,那么它的终结器将已经运行)。这些可以通过一个或多个幻象引用对象从根访问
  7. 不可达对象-对象既不是强、软、弱,也不是幻影可达的,也不是可复活的。这些对象已准备好回收

更多详情:https://www.artima.com/insidejvm/ed2/gc16.html"崩溃

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

唯一真正的区别

每个该医生,松散的Weak引用必须被正在运行的GC清除。

根据该医生,在抛出OOM之前清除松散的SoftRe的必须

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

软引用很有用。内存敏感的缓存使用Soft引用,而不是WeakRe的。


WeakReference的唯一适当用途是观察GC运行。你可以通过创建一个新的WeakReference来做到这一点,它的对象立即超出范围,然后尝试从weak_ref.get()中获取null。当它是null时,你会发现在这段时间之间,GC运行了。

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

  • 一个糟糕的黑客来实现优先级-2的软引用,这样你就不必编写一个,然而它并没有像预期的那样工作,因为缓存会在每次GC运行时被清除,即使有备用内存。参见https://stackoverflow.com/a/3243242/632951for phails。(此外,如果你需要超过2级的缓存优先级怎么办?你仍然需要一个真正的库来实现它。)

  • 将数据与现有类的对象关联起来的糟糕黑客,然而当你的GC在创建弱引用后决定休息时,它会创建内存泄漏(OutOfMemoryError)。

  • 将数据与现有类的对象关联起来的糟糕黑客,其中类有勇气使自己不可子类化,并用于你需要调用的现有函数代码。在这种情况下,正确的解决方案是编辑类并使其可子类化,或者编辑函数并使其采用接口而不是类,或者使用替代函数。

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

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

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

  2. 堆中的空闲内存量。

    示例:

    • 堆有10MB的可用空间(在完整的GC之后);
    • -XX:软参考L RU策略M SPerM B=1000

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

这个文章对于理解强、软、弱和幻象引用非常有帮助。


给你一个总结,

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

如果对象只有软引用(没有强引用),那么只有当JVM运行内存溢出时,GC才会回收该对象。


所以你可以说,强引用有终极力量(永远不能被GC收集)

软引用比弱引用强大(因为它们可以逃脱GC循环,直到JVM运行内存溢出)

弱引用比软引用甚至更弱(因为它们不能释放任何GC循环,如果对象没有其他强引用,它们将被回收)。


餐厅类比

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

现在,如果你是强大的客户(类似于强引用),那么即使餐厅来了一位新顾客或者发生了什么,你也永远不会离开你的桌子(堆上的内存区域)。服务员没有权利告诉你(甚至要求你)离开餐厅。

如果你是软客户(类似于软引用),那么如果餐厅来了一位新顾客,服务员不会要求你离开桌子,除非没有其他空桌子可以容纳新顾客。(换句话说,只有当新顾客进来,没有其他桌子留给这个新顾客时,服务员才会要求你离开桌子)

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

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

重载下的堆和GC行为

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

你可以得到更多的深度这个实验的图表,统计数据,观察结果