View + Tag =内存泄漏?

时间:2011-03-23 20:04:50

标签: android memory-leaks view

基础:

  • 活动 - 在每次取向变化时重新创建(onCreate-onDestroy)
  • View包含带有两个子项的ViewFlipper:简单的RelativeLayout和ListView
  • ListView行具有复杂的布局和关联标记

问题是我在每个方向更改时都有内存泄漏 - 活动在整个视图布局中保留在内存中。活动本身就是一个上下文,所以只要关联对象就会保留在内存中。所以现在我想找到泄密事件发生的原因。

View具有 setTag()方法。我用它来存储一些关于行的信息(因此ListView中的每一行(View)都有关联的标签)。

但是视图和GC如何处理标签?我的标记对象(持有者)包含对视图的引用,但如果视图删除了对它的标记的引用,则可以很容易地收集引用(带有标记本身)。

有人遇到类似ListViews的问题吗?

P.S。我想知道GC如何清理布局 - 循环引用,上下文,持有者等等......

4 个答案:

答案 0 :(得分:9)

首先,如果使用View.setTag(int, Object)方法,可以泄漏对象。使用此方法设置的标记存储在静态WeakHashMap中,其中View为关键字。因此,如果您在父视图的标记中存储对子视图的引用,那么所有这些视图以及它们(父活动)创建的上下文都将被泄露。这是因为每个子视图都包含对其父视图的引用,因此GC将永远不会收集父视图。

有一种简单的方法可以模拟这种行为:

public static class MainActivity extends ListActivity {
    private final WeakHashMap<Parent, Parent.Child> mMap =
        new WeakHashMap<Parent, Parent.Child>();

    @Override
    public void onCreate(final Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        // If parents were collected OOM error wouldn't be thrown.
        // But they aren't collected so we get OOM here.
        for (int i = 0; i < 10; ++i) {
            Parent parent = new Parent();
            mMap.put( parent, parent.mChild );
        }
    }
}

public static class Parent {
    public final Child mChild = new Child();

    public class Child {
        private final byte[] mJunk = new byte[10*1024*1024];
    }
}

其次,似乎ListView类导致内存泄漏。这意味着列表视图,其所有可回收的子项及其父活动都被泄露。以下是有关此错误的一些信息:

答案 1 :(得分:4)

我认为你可能在某处有一些非静态的内部类,它总是保存指向它们周围对象实例的指针。例如:

public class A {

    private class B {
        // ...
    }

    // b stores a reference to the instance of A
    private B b = new B();
}

如果使用setTag()方法(例如,对于ViewHolder类),则永远不要存储对父对象的任何引用。实际上,您应该将此类声明为静态。

另外,为了避免内存泄漏,如果可能的话,你应该总是将getApplicationContext()的结果传递给需要Context的方法 - 而不是对Activity本身的引用。

答案 2 :(得分:3)

很容易泄漏对方向更改的活动的引用。有一些关于此的博客文章 - 我认为需要阅读:

http://ttlnews.blogspot.com/2010/01/attacking-memory-problems-on-android.html

http://android-developers.blogspot.com/2009/01/avoiding-memory-leaks.html

http://code.google.com/p/android/issues/detail?id=2391

onRetainNonConfigurationInstance方法的一个超级简介中,您只需要注意忽略对View对象的任何引用,然后依次调用Activity引用,进度条等。

我使用的一个好模式是有一个“StateHolder”内部类,它确实包含一个Activity引用,但我实现了一个setActivityForTasks方法,我只是将NULL传递给它,它又将所有Activity引用设置为NULL 。然后,当您在更改方向后返回活动时,您只需拨打setActivityForTasks(this)即可重置当前活动。

单一外卖只是对onRetainNonConfigurationInstance

中与任何活动相关的任何引用的NULL

答案 3 :(得分:2)

在Gingerbread和Android的较低版本中,View.setTag (int key, Object tag)泄漏内存。不要使用它。它已在ICS中修复。