The basis:
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...
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:
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With