查看 +标签=内存泄漏?

发布于 2024-10-26 16:03:54 字数 540 浏览 1 评论 0原文

基础:

  • Activity - 在每个方向更改时重新创建(onCreate-onDestroy)
  • View 由 ViewFlipper 和两个子级组成:简单的relativelayout和listview
  • listview行具有复杂的布局和关联的标签

问题是我在每次方向更改时都有内存泄漏 - 活动保持不变在内存中具有整个视图布局。活动本身就是一个上下文,因此只要关联的对象在内存中,它就会保留在内存中。所以现在我正在尝试找出泄漏发生的原因。

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

但是视图和 GC 如何与标签一起工作呢?我的标签对象(持有者)包含对视图的引用,但如果视图删除对其标签的引用,则该引用(带有标签本身)将很容易被收集。

有人在使用 ListViews 时遇到过类似的问题吗?

PS 我想知道 GC 如何清理布局 - 大量的循环引用、上下文、持有者等......

The basis:

  • Activity - recreates(onCreate-onDestroy) on each orientatin change
  • View consists of ViewFlipper with two childs: simple RelativeLayout and ListView
  • ListView rows have complex layout and associated tags

The problem is that i have memory leak on each orientation change - activity stays in memory with whole view layout. Activity itself is a context so it'll stay in memory as long as associated objects will. So now i'm trying to find why leaks are happen.

View has setTag() method. I'm using it to store some information about rows(so every row(View) in ListView has associated tags).

But how does views and GC acts with tags ? My tag objects(holders) contains references to views but if view removes reference to it's tag this references(with tag itself) will be easily collected.

Anybody have faced similar problems with ListViews ?

P.S. i'm wondering how GC cleans layouts - tonns of cyclic references, contexts, holders, etc...

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

夜血缘 2024-11-02 16:03:54

首先,如果使用 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 类似乎会导致内存泄漏。这意味着列表视图、其所有回收的子活动及其父活动都被泄漏。以下是有关此错误的一些信息:

Firstly you can leak objects if you use View.setTag(int, Object) method. Tags set using this method are stored in a static WeakHashMap with a View as a key. So if you store references to child view in the parent view's tags then all these views and a context they were created with (parent activity) will be leaked. It happens because every child view holds a reference to its parent, so the parent view will never be collected by the GC.

There's a simple way to simulate this behavior:

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];
    }
}

Secondly it seems that the ListView class causes a memory leak. It means that the list view, all its recycled children and its parent activity are leaked. Here's some information about this bug:

献世佛 2024-11-02 16:03:54

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

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 本身。

I think you might have some non-static inner classes somewhere, which always save a pointer to their surrounding object instance. For example:

public class A {

    private class B {
        // ...
    }

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

If you use the setTag() method (e.g. for a ViewHolder class), never store any references to parent objects there. In fact, you should declare this class static.

Plus, to avoid memory leaks, if possible you should always pass the result of getApplicationContext() to methods that require a Context - and no reference to the Activity itself.

弄潮 2024-11-02 16:03:54

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

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< /a>

简而言之,在您的 onRetainNonConfigurationInstance 方法中,您只需要小心清空对 View 对象的任何引用,进而清空 Activity 引用、进度条等。

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

唯一的要点就是将 onRetainNonConfigurationInstance 中与任何 Activity 相关的任何引用置为 NULL

Its easy to leak references to the Activity on orientation change. There are a handful of blog posts about this - which I feel are required reading:

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

In a super nutshell in your onRetainNonConfigurationInstance method you just want to be careful that you null out any references to View objects and in turn Activity references, Progress bars, etc.

A good pattern I use is having a "StateHolder" inner class which does contain an Activity reference, but I implement a setActivityForTasks method, which I just pass NULL to, it in turn sets all Activity references to NULL. Then when you're going back through your Activity after the orientation change you can just call setActivityForTasks(this) to reset the current activity.

The single take-away is just to NULL out any references to anything Activity related in onRetainNonConfigurationInstance

茶花眉 2024-11-02 16:03:54

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

In Gingerbread and lower versions of Android, View.setTag (int key, Object tag) leaks memory. Do not use it. It was fixed in ICS.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文