diff options
Diffstat (limited to 'docs/html/resources/articles/avoiding-memory-leaks.jd')
-rw-r--r-- | docs/html/resources/articles/avoiding-memory-leaks.jd | 109 |
1 files changed, 109 insertions, 0 deletions
diff --git a/docs/html/resources/articles/avoiding-memory-leaks.jd b/docs/html/resources/articles/avoiding-memory-leaks.jd new file mode 100644 index 0000000..3361bc1 --- /dev/null +++ b/docs/html/resources/articles/avoiding-memory-leaks.jd @@ -0,0 +1,109 @@ +page.title=Avoiding Memory Leaks +@jd:body + + +<p>Android applications are, at least on the T-Mobile G1, limited +to 16 MB of heap. It's both a lot of memory for a phone and yet very +little for what some developers want to achieve. Even if you do not +plan on using all of this memory, you should use as little as possible +to let other applications run without getting them killed. The more +applications Android can keep in memory, the faster it will be for the +user to switch between his apps. As part of my job, I ran into memory +leaks issues in Android applications and they are most of the time due +to the same mistake: keeping a long-lived reference to a +{@link android.content.Context Context}.</p> + +<p>On Android, a <code>Context</code> is used for many operations + but mostly to load and access resources. This is why all the widgets +receive a <code>Context</code> parameter in their constructor. In a +regular Android application, you usually have two kinds of +<code>Context</code>, {@link android.app.Activity} and +{@link android.app.Application}. It's usually the first one that +the developer passes to classes and methods that need a <code>Context</code>:</p> + +<pre class="prettyprint">@Override +protected void onCreate(Bundle state) { + super.onCreate(state); + + TextView label = new TextView(this); + label.setText("Leaks are bad"); + + setContentView(label); +} +</pre> + +<p>This means that views have a reference to the entire activity and +therefore to anything your activity is holding onto; usually the entire +View hierarchy and all its resources. Therefore, if you leak the <code>Context</code> +("leak" meaning you keep a reference to it thus preventing the GC from +collecting it), you leak a lot of memory. Leaking an entire activity +can be really easy if you're not careful.</p> + +<p>When the screen orientation changes the system will, by default, +destroy the current activity and create a new one while preserving its +state. In doing so, Android will reload the application's UI from the +resources. Now imagine you wrote an application with a large bitmap +that you don't want to load on every rotation. The easiest way to keep +it around and not having to reload it on every rotation is to keep in a +static field:</p> + +<pre class="prettyprint">private static Drawable sBackground; + +@Override +protected void onCreate(Bundle state) { + super.onCreate(state); + + TextView label = new TextView(this); + label.setText("Leaks are bad"); + + if (sBackground == null) { + sBackground = getDrawable(R.drawable.large_bitmap); + } + label.setBackgroundDrawable(sBackground); + + setContentView(label); +} +</pre> + +<p>This code is very fast and also very wrong; it leaks the first activity +created upon the first screen orientation change. When a +{@link android.graphics.drawable.Drawable Drawable} is attached to a view, the view is set as a +{@link android.graphics.drawable.Drawable#setCallback(android.graphics.drawable.Drawable.Callback) callback} +on the drawable. In the code snippet above, this means the drawable has a +reference to the <code>TextView</code> which itself has a reference to the +activity (the <code>Context</code>) which in turns has references to +pretty much anything (depending on your code.)</p> + +<p>This example is one of the simplest cases of leaking the +<code>Context</code> and you can see how we worked around it in the +<a href="http://android.git.kernel.org/?p=platform/packages/apps/Launcher.git;a=blob;f=src/com/android/launcher/LauncherModel.java;h=0ef2a806b767142b28b2ff3b37f21f4ca16c355d;hb=cupcake">Home screen's source code</a> +(look for the <code>unbindDrawables()</code> method) by setting the stored +drawables' callbacks to null when the activity is destroyed. Interestingly +enough, there are cases where you can create a chain of leaked contexts, +and they are bad. They make you run out of memory rather quickly.</p> + +<p>There are two easy ways to avoid context-related memory leaks. The most +obvious one is to avoid escaping the context outside of its own scope. The +example above showed the case of a static reference but inner classes and +their implicit reference to the outer class can be equally dangerous. The +second solution is to use the <code>Application</code> context. This +context will live as long as your application is alive and does not depend +on the activities life cycle. If you plan on keeping long-lived objects +that need a context, remember the application object. You can obtain it +easily by calling +{@link android.content.Context#getApplicationContext() Context.getApplicationContext()} +or {@link android.app.Activity#getApplication() Activity.getApplication()}.</p> + +<p>In summary, to avoid context-related memory leaks, remember the following:</p> +<ul> +<li>Do not keep long-lived references to a context-activity (a reference +to an activity should have the same life cycle as the activity itself)</li> +<li>Try using the context-application instead of a context-activity</li> +<li>Avoid non-static inner classes in an activity if you don't control +their life cycle, use a static inner class and make a weak reference to +the activity inside. The solution to this issue is to use a static inner +class with a {@link java.lang.ref.WeakReference WeakReference} to the +outer class, as done in <a href="http://android.git.kernel.org/?p=platform/frameworks/base.git;a=blob;f=core/java/android/view/ViewRoot.java;h=9d7a124cb01ab94bf53e34f6e5e8a07f81e2423c;hb=master">ViewRoot</a> +and its W inner class for instance</li> +<li>A garbage collector is not an insurance against memory leaks</li> +</ul> |