I have written the following class inside one of my adapters. The class gets a view from the activity inside its constructor:
private class ContactViewHolder {
public TextView tvPrimary;
public TextView tvSecondary;
public ImageView imageViewCheck;
public ContactViewHolder(View v) {
this.tvPrimary = (TextView) v.findViewById(R.id.tv_primary);
this.imageViewCheck = (ImageView) v.findViewById(R.id.iv_check);
v.findViewById(R.id.iv_mm_user);
this.tvSecondary = (TextView) v.findViewById(R.id.tv_secondary);
}
}
Will this lead to a memory leak issue?
It seems it might cause a memory leak, as described in this link, regarding a slightly different issue.
So, to sum up, I want to know:
Will this cause a memory leak issue?
If so, why?
If you define a instance like this:
static ContactViewHolder holder = new ContactViewHolder(view);
It will result in a memory leak.
If you define a non-static instance, it has no memory leak.
The reason of leak is as follows:
No-static inner class will hold a reference of outter class instance, so a static holder will hold a reference of your adapter.
As is known to all, static instance can not be collected by GC, it's a gc root, so your adapter can not be collected until the process is terminated.
Memory leaks occures when you dont know/control the lifecycle of the inner class object. Because its not being collected by the GC then the object the holds him doesn't get collected and so on and that causes a memory leak. Basically if your holder only handles with the ui in the simplest way like you wrote then there is no way that he will "outlive" the adapter that holds him then it will not cause a memory leak.
If for some reason you are implementing onClick listener inside the Holder which open an AsyncTask for example then you dont control the life-cycle of the object because it opens a new Thread and might be doing some operation while the adapter is no longer on the UI and that will cause a memory leak.
Conclusion:
Be very aware of the lifecycle of your objects and who owns them.
Related
I have a class which has a static variable which holds Context reference.
public static Context context;
Android studio gives a warning saying that static references to the
Context class leads to memory leaks
If I remove the static keyword, it does not give any warning.
Similar scenario with ContextWrapper class as well.
My understanding is, if we hold a reference to the classes which are related to Context will lead to memory leak. But, Why Android studio does not give any warning for non-static Context references?
I have seen some code examples where they have extended the ContextWrapper class. Whenever they needed the Context, they are accessing through the class which was extended `ContextWrapper'. Will this approach does not lead to Memory leak?
Also, Will memory leaks happens for non-static Context references at runtime? Did I understand it in wrong way? Am I missing something here?
Can anyone give explanation for this?
Thanks in advance.
First things first, let's know about memory leaks and why it happens exactly
Memory leaks occur if there is any data in memory which can't be garbage collected ,Having said that, static variables can't be garbage collected as they stays live in memory throughout the Application life where as non-static variables can be garbage collected once it's enclosing parent is no longer referenced, keeping this in mind we'll see example to explain your question.
Consider class A with a static and non static Variable
Class A{
private static Context iCanNeverBeDestroyed;
// Scope -> Application life, so memory leak can occur
private Context iCanBeDestroyedWithA;
// Scope -> A
private static int itWontMatterWhetherImDestroyedOrNot;
//Even though the scope is Application, due to low memory usage , memory leak is negligible in this case
}
Leakage is calculated based on scope of variable and size in memory together, high memory and greater scope has greater chances of leak, example like Context,Bitmap etc
suppose I have an activity, and it contains a TextView. I can initialize the TextView either to a member variable or to a local variable. is there any memory wise difference between these to initialization ?
example :
Activity with local view reference:
public class MainActivity extends Activity{
#OVerride
public void onCreate(Bundle b){
TextView textView = (TextView)findViewById(R.id.my_text_view_id);
}
}
Activity with member view reference:
public class MainActivity extends Activity{
TextView mTextView;
#OVerride
public void onCreate(Bundle b){
mTextView = (TextView)findViewById(R.id.my_text_view_id);
}
}
You should always use the minimal scope. So when you declare a variable you should ask yourself:
"Will I need this variable later in a different function?"
Yes -> Use a member variable
No -> Use a local variable
Edit:
What also to consider is the cost of object creation:
If a function does get called repeatedly it is a good practice to instanatiate an object only once, store it as a member variable and reuse it instead of creating a new instance every time the function gets called.
So the 2nd important question is:
"Will this function get called a lot and do I really need a new instance of the object stored in the variable?"
Yes, often, and no, I can reuse the same object over -> use a member variable. This way the same memory is used and no garbage gets piled up. Use only for large Arrays or objects, it is not needed for simple int vars in loops.
Memory wise global variables are much more prone to memory leaks. The scope any variable depends on its scope. For local variable, the scope is closing braces of the respected method, and variable is automatically garbage collected after the execution of closing braces. Where as global variable will reside in memory until the any object of that class is in memory.
Generally, try to avoid using 'global' variables unless you have good
reason to do .
You can use Local or Global Variable depends upon your Requirement .
Mainly, local variables are that they work within a limited scope means they are declared when a function is called and after ending it the memory taken up by the variable is released.
Finally usage of global variables leads to wastage of memory .
With this question the main concern is what could be best approach. In my android app an Application class is subclassed mainly for two reasons,
1), Maintaining the global state across the app,
2), Ability to initialise and use SharedPreferences in non-activity classes.
The code is somewhat like this,
public class GlobalApp extends Application {
private static GlobalApp instance;
private Bitmap bitmap;
#Override
public void on create(){
super.on create();
instance = this;
}
public static GlobalApp getInstance() {
return instance;
}
public void setBitmap(Bitmap b) {
this.bitmap = b;
}
}
As you can see, to have access of application context in non-activity class, it has been taken as the static whereas other fields are simply instance variables, in which we are storing Bitmaps, will it cause the memory leak ?
And above approach is best? Or it can be made even better than this?
Please help.
I don't see anything there that could cause a memory leak. However, you needn't store and retrieve the instance like that, you can just do this:
GlobalApp app = ((GlobalApp)this.getApplication());
Just make sure to add the application class name to the manifest. Keep in mind also that just because there isn't a memory leak doesn't mean there isn't a memory problem. If you hold on to too much memory in that Application object, your app could crash with an OutOfMemoryError. Depending on your exact needs you may need to use weak references to avoid that. More info about using the Application class: Using the Android Application class to persist data
I've been studying up on best practices for preventing Context/Activity memory leaks when creating views, and I can't seem to find a definite answer on what is or is not allowed when it comes to static fields in classes.
Let's say I have a code of this form:
public class MyOuterClass extends Activity{
private MyInnerClass;
MyInnerClass = (MyInnerClass) findViewById(<XML call here>);
MyInnerClass.myXInt = 3;
// onCreate(), onResume(), etc.
public static class MyInnerClass extends SurfaceView implements Runnable{
// Safe variables?
private static int myXInt, myYInt;
private static boolean myBoolean;
// Potentially safe?
private static Canvas myCanvas;
// Definitely bad.
private static Context myContext;
public MyInnerClass(Context context){
myContext = context; // This is bad.
}
}
}
I am slightly confused on what the JVM actually considers the ClassLoader for MyInnerClass. Technically, since it is a SurfaceView object, it seems like the static variables should always exist once the application has instantiated MyInnerClass one time (which happens when the View is first inflated), and then remain there until the application itself is terminated. If that is the case, what prevents Bitmaps and Canvas objects from remaining open as well and filling up the heap?
The only statement I ever see repeated over and over is that you can't leak static Context like I have shown in the constructor, but it never goes beyond that. Is that really the only thing you can't do?
In Java/Android a static variable or constant will not be garbage collected. It just stays there once the class that holds it is loaded via a class loader. The class loader is afaik always the same for all classes inside your app and its the one that has static references to all your classes (to e.g. MyInnerClass.class). Since the class loader does not go away your classes won't do that either since they are referenced & therefore not garbage collectable.
Like in your example
public class SomeClass extends SurfaceView {
private static Context myContext;
public MyInnerClass(Context context){
myContext = context; // This is bad.
}
}
That is indeed bad. Even if no reference to SomeClass exists (e.g. the Activity that showed your custom SurfaceView has ended) the static reference to the Context (and any other static variable / constant in SomeClass will remain. You can consider all of them leaked since it is not possible to garbage collect that Context etc. If you have a regular variable reference something then once the instance that contains that variable has no more references to it the whole instance including its references to other things can and will be garbage collected. Java can even handle circular references fine.
For constants you want that to happen and it is usually not bad since the amount of constants and the amount of memory they occupy is not large. Also constants don't (should not) reference other instances that take up large amounts of memory like Context or Bitmap.
Besides the possibility to create memory leaks through static variables you may also create problems if you don't want to have only a single thing for all instances at the same time. For example if you save the Bitmap of your SurfaceView in a static variable you can't have two different images. Even if the two SurfaceViews are not displayed at the same time you could run into problems since each new instance will probably overwrite the old image and if you go back to the other SurfaceView you unexpectedly show the wrong image. I am almost sure you don't want to use static here.
The fact that your inner class is a static class does not mean that you have to use static variables - it just means that it behaves more like a static method since it can't use the instance variables (the ones that are not static) in your class.
To avoid memory leaks you simply should not use static variables at all. There is no need to use them unless you do special stuff (e.g. counting instances of a class). Constants are fine.
This article talks about mutable static fields: http://javabook.compuware.com/content/memory/problem-patterns/memory-leaks.aspx. Basically, avoid them and use constants instead.
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...
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:
http://code.google.com/p/android/issues/detail?id=12334
Android: AlertDialog causes a memory leak
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.
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
In Gingerbread and lower versions of Android, View.setTag (int key, Object tag) leaks memory. Do not use it. It was fixed in ICS.