Java - WeakReference最佳实践

时间:2014-02-26 09:20:57

标签: java garbage-collection weak-references

我将在这个问题上加上我不熟悉Java垃圾收集的声明,所以如果收集器解决了这个问题,我会对此感到高兴。或者,如果我无聊的Java内存分配,我实际上是,我道歉。但是,我正在考虑以下三种情况:

public class ScenarioA implements MyQuestion{

    private Field field;

    public Field getField(){
        if(field == null){
           field = new Field(this);
        }
        return field;
}

VS

  public class ScenarioB implements MyQuestion{

    public Field getField(){
        return new Field(this);
    }
}

VS。

import java.lang.ref.WeakReference;

public class ScenearioC implements MyQuestion{

    private WeakReference<Field> weakField;

    public Field getField(){
        if(WeakField == null || weakField.get() == null){
            weakField = new WeakReference(new Field(this));
        }
        return weakField.get();
    }
}

我认为ScenarioA很糟糕,因为如果在.get()的实例上调用ScenarioA,我们会在ScenarioA的实例之间保持强引用Field方法返回的.get()实例,意味着无论是否实际关注任何一个,都不会被垃圾收集。

ScenarioB存在可能实例化大量.equal个对象的问题,这可能非常昂贵且不必要。

ScebarioC我至少理解。我试图阅读WeakReferencehere)的来源,但我无法弄清楚发生了什么。我想责怪在幕后发生的某些GC / VM操作,但我想在很多其他事情上责怪很多事情。无论如何,在我看来,每个WeakReference必须严格要求更多的内存而不是简单地维护对引用的引用,因为我们必须首先保持对WeakReference的引用,然后保持对实际的引用宾语。 (我为那句话道歉)。如果Field很大和/或我们实例化了几个ScenarioA,这可能会很糟糕。更重要的是,在我看来,每个WeakReference都需要自己的线程,除非实例ScenarioC - 因此弱引用 - 死亡,否则它本身(线程)似乎永远不会消亡。如果ScenarioC有很多实例,那可能会非常糟糕。

有没有人能解决这个问题?

3 个答案:

答案 0 :(得分:2)

在考虑使用WeakReference时,你应该问问自己,如果没有强烈的目标引用,立即无效,你会有什么感受。在WeakReference适合的大多数地方,这种行为是可取的;如果在特定情况下这种行为是不受欢迎的,那通常表明某种其他形式的缓存会更合适。

从根本上说,WeakReference通常不适合建立与目标对象本身的连接,而是建立与引用目标对象的其他内容的间接连接。当没有其他东西引用目标对象时,弱引用将变得无用,应该被消除。

例如,考虑一个对象Joe,其目的是让任何有兴趣的人知道对象Fred执行某些操作的次数。它要求Fred在它执行相关操作时让它知道(通过调用方法),并且每次调用该方法时,Joe都会递增计数器。在这种情况下,Fred要么对Joe持有弱引用,要么保留对Joe的弱引用的引用。毕竟,弗雷德并不关心这个柜台 - 它唯一的兴趣是确保每个想知道其行为的人都会发现它们。 Fred对Joe本身并不感兴趣,而是因为其他实体可能对Joe感兴趣。一旦没有其他人对Joe感兴趣,Fred就没有理由。

关于您的示例,如果使用与先前请求相同的对象填充将来请求的好处主要源于对该对象的其他引用的存在,则弱引用将是适当的。如果不了解有关对象的使用模式,就无法确定它们是否符合该描述。然而,这是我在决定是否使用弱引用时所关注的问题。

答案 1 :(得分:0)

我建议使用WeakReference的最佳做法的唯一建议是不要使用,否则可能需要注意,除非你真的必须。他们无法帮助你做什么。

您似乎担心制作太多对象 - 这是Java新手的常见错误。制作对象并释放它们是Java中的一个基本机制,并且已被调整为异常高效。您需要担心的唯一考虑因素是对象是否需要花费大量时间来构建或占用大量空间。在这种情况下,您应该考虑使用池(可能在幕后使用WeakReference)。

要考虑的另一个选择是使用Singleton

答案 2 :(得分:0)

您无法通过查看其源代码来推导WeakReference的工作方式。垃圾收集器知道WeakReference类的特殊语义并相应地处理其referent。当你真的需要使用WeakReference时,你必须知道它的get方法总是可以返回null,只要没有普通(又强)引用它referent即使您刚刚检查了get之前的返回值,即使您刚刚在它之前创建了WeakReference,也是如此。

您必须使用强引用,例如在局部变量中,保留新实例或在返回之前检查WeakReference.get()返回的实例:

import java.lang.ref.WeakReference;

public class ScenarioC implements MyQuestion {

    private WeakReference<Field> weakField;

    public Field getField() {
      if(weakField!=null) {
        Field field = weakField.get(); // create an ordinary strong reference
        if(field!=null) return field; // the strong reference works as known
      }
      Field field=new Field(this); // keep the new instance ordinarily
      weakField = new WeakReference<Field>(field);
      return field; // return the ordinary strong reference to the new instance
    }
}

请注意,此代码仍然不是线程安全的,它仅显示在使用WeakReference时如何处理可能的垃圾回收。