2014-02-26 78 views
0

我将用一个声明来说明这个问题,即我是Java垃圾收集的新手,所以如果收集器处理这个问题,我会很高兴。或者,如果我是可悲的无知的Java内存分配,我实际上是,我很抱歉。但是,我正在考虑以下三种情形:Java - WeakReference最佳实践

public class ScenarioA implements MyQuestion{ 

    private Field field; 

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

public class ScenarioB implements MyQuestion{ 

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

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的实例和的实例之间保持强烈的引用由.get()方法返回,这意味着既不会被垃圾收集,也不管程序实际上是否关心任何一个。

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

ScebarioC我明白至少。我试图读取WeakReferencehere)的来源,但我无法弄清楚发生了什么。我想指责在幕后发生的某些GC/VM操作,但是我想把很多事情归咎于许多其他事情。无论如何,在我看来,每个WeakReference都必须要求更多的内存,而不是简单地维护对象的引用,因为我们必须首先保留对WeakReference的引用,然后引用实际对象。 (我对这句话表示歉意)。如果Field很大并且/或者我们实例化了几个ScenarioA's,这可能是不好的。更重要的是,在我看来,每个WeakReference都需要自己的线程,除非实例ScenarioC - 因此弱参考 - 死亡,否则它本身(线程)似乎永远不会死亡。如果有很多ScenarioC的实例,这可能会非常糟糕。

有没有人有解决这个问题的办法?

+0

场景A有问题,它不是线程安全的;如果线程安全是一个问题,不要这样做。 – fge

+0

您的方案C可能会返回null,因为GC可能在收到weakField.get()之前收集弱引用对象。下面的答案有一个使用弱引用的更正确的版本。 – Gamlor

回答

0

我建议最佳做法WeakReference唯一的建议是没有使用这些可能的警告,除非你真的要。他们不会帮助你做什么。

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

另一种可供选择的方法是使用Singleton

0

您无法通过查看其源代码来推导WeakReference的工作方式。垃圾收集器知道WeakReference类的特殊语义并相应地处理它的referent。如果您确实需要使用WeakReference,则必须注意,只要不存在对其referent的普通(强称)引用,即使您刚刚检查了返回值get之前,它的get方法始终可以返回null ,即使您刚刚在它之前创建了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

2

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

从根本上讲,WeakReference通常不适合用于建立到所述目标对象本身的连接,而是用于建立的间接连接将其作为参考目标对象的其他事情。当没有其他东西引用目标对象时,弱引用将变得无用,应该被消除。

例如,考虑一个对象Joe,其目的是让任何感兴趣的人知道对象Fred做了些什么。它要求Fred让它知道(通过调用一个方法),每当它执行有问题的动作,并且每次调用该方法时,都会增加一个计数器。在这种情况下,Fred应该对Joe提供一个弱引用,否则应该引用一个对Joe有弱引用的东西。毕竟,弗雷德并不在意柜台 - 唯一的利益就是确保每个想了解其行为的人都能了解它。弗雷德并不是真的对乔本身感兴趣,而是因为其他实体可能对乔感兴趣。一旦没有其他人对乔感兴趣,弗雷德应该是没有理由的。

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