使用WeakReferences有什么好处?

时间:2011-05-24 20:25:01

标签: java android memory-leaks bitmap weak-references

我的应用中有一些内存泄漏。它们都是围绕一个特定的视图集群发起的,我花了很多时间进行调整,并试图尽可能减少大量的上下文传递。这让我相信群集中使用的位图是个问题。所以我想将WeakReferences用于对视图使用的位图的所有引用。我从未使用过WeakReference,也不确定这是否是一个很好的应用程序。任何机构都可以提供有用的指示或提示吗?

4 个答案:

答案 0 :(得分:15)

  

所以我想要使用   所有引用的WeakReferences   视图使用的位图。我有   从来没有使用WeakReference而不是   确定这是一个很好的应用程序。   任何身体都可以提供帮助   指针或提示?

小心,这在你的情况下很危险。当您的应用程序可能仍需要它时,GC可以清除所有位图。

关于WeakReference的关键问题是了解与硬引用的区别。如果应用程序中没有对位图的硬引用,则允许GC以原子方式从内存中删除对象,并且所有现有的弱引用将立即指向null。在您的情况下,您不能在代码中使用弱引用。

以下是解决方案的概念。创建一个容器对象,它将对所有位图保留弱引用(仅限)。您的视图应始终仅使用硬引用引用位图。当视图创建位图时,它应该在容器对象中注册它。当它想要使用视图时,它应该从容器中获取硬引用。

就像那样,如果没有视图引用位图,那么GC将收集对象而没有视图的副作用,因为没有一个硬件引用它。使用弱引用对象时,最好在不再需要对象时将硬引用显式设置为null。

<强>加成

以下是解决方案的快速实现(仅提供一个想法):

public class BitmapContainer {

    public static class Bitmap {
        private final long id;
        public Bitmap(long id) { this.id = id; }
        public long getId() { return id; }
        public void draw() { };
    }

    WeakHashMap<Bitmap, WeakReference<Bitmap>> myBitmaps
        = new WeakHashMap<Bitmap, WeakReference<Bitmap>>();

    public void registerBitMap(Bitmap bm) {

        if ( bm == null ) throw new NullPointerException();

        WeakReference<Bitmap> wr = new WeakReference<Bitmap>(bm);
        myBitmaps.put(bm, wr);

    }

    /** Method returns null if bitmap not available */
    public Bitmap getBitMap(long id) {

        for ( Bitmap item : myBitmaps.keySet() ) {
            if ( item != null) {
                if ( item.getId() == id ) {
                    return item;
                }
            }
        }

        return null;

    }

}

答案 1 :(得分:4)

我能想到的最简单的弱引用使用是缓存。您希望将对象添加到缓存中,但如果在VM的其余部分中没有对该对象的引用,则您希望该对象获得GC,而无需返回并自行将其从缓存中删除。弱引用实现了这一点。您在缓存中添加对象的弱引用。当缓存是唯一引用您的对象的东西时,它符合GC的条件。尝试在对象GC之后使用弱引用会导致异常。

严格地说,当没有引用它时,对象符合GC的条件(即是否存在对它的任何弱引用)。

根据您对您的情况的描述,不清楚弱参考会对您有所帮助。但是,如果您遇到需要有意清除对不再需要的对象的引用的情况,那么弱引用可能就是解决方案。你只需要确保当只有弱引用时,摆脱对象真的是可以的。

答案 2 :(得分:3)

对WeakReferences的需求来自于您需要维护有关您无法控制的对象的元数据的场景。

一个人为的例子是String,它是最终的,我们无法扩展它,但是如果我们想保留一些关于特定String实例的额外数据,我们可能会使用{ {1}}将保存此元数据的实现。对于这个例子,我建议我们要保持字符串的长度作为我们的元数据(是的,我知道Map对象已经有一个公共长度属性)。所以我们会像这样创建一个String

Map

假设我们可能会在某种方法中填充此映射,并且不知道我们何时完成数据,因此我们无法显式删除这些条目。当我们填充这个永远不会无人居住的地图时,我们的引用将永远保留。如果应用程序运行很长时间,我们很可能会遇到OutOfMemoryError。

对此的解决方案是使用Map<String, Integer> stringLengths = new HashMap<String, Integer>(); 实现。

WeakHashMap

这样,当对密钥的所有(强)引用都消失时,下一个GC将导致Map<String, Integer> stringLengths = new WeakHashMap<String, Integer>(); 的条目被删除。 (是的,我理解WeakHashMap在JVM的核心有一个特殊的位置,但我假设String的GC与普通的Object在这个人为的例子中的方式相同)

如果这是您在应用中使用的方法(将位图存储在全局地图中),我认为这绝对值得关注。

答案 3 :(得分:1)

我认为这不是解决问题的正确方法。正如其他人所说,如果你使用WeakReferences,你会使代码更昂贵,更脆弱。发生脆弱性是因为每次使用弱引用时都可能会出现异常。

(另一个问题是WeakReferences比GC的常规引用更昂贵。我没有任何实际的性能数据,这很可能与你的用例无关,但至少这个一个理论上的问题。)

IMO,更好的解决方法是使用一个好的内存分析器来追踪内存泄漏实际发生的位置并修复它。使用内存分析器运行应用程序一段时间,识别已泄漏的某个对象,并使用分析器跟踪仍可访问对象的路径。您可能会发现这可以追溯到一个或两个错误,或者在一些地方重复相同的错误模式。 (我的猜测是在正确的时间没有删除的事件监听器。)