The Memory Profiler is a component in the Android Profiler that helps you identify memory leaks and memory churn that can lead to stutter, freezes, and even app crashes. It shows a realtime graph of your app's memory use and lets you capture a heap dump, force garbage collections, and track memory allocations.
Memory profilers are tools that can monitor memory usage and help detect memory leaks in an application. Profilers can also help with analyzing how resources are allocated within an application, for example how much memory and CPU time is being used by each method.
The primary tools for detecting memory leaks are the C/C++ debugger and the C Run-time Library (CRT) debug heap functions. The #define statement maps a base version of the CRT heap functions to the corresponding debug version. If you leave out the #define statement, the memory leak dump will be less detailed.
Causes of Memory Leaks and Their SolutionsOne should not use static views while developing the application, as static views are never destroyed. One should never use the Context as static, because that context will be available through the life of the application, and will not be restricted to the particular activity.
One of the most common errors that I found developing Android Apps is the “java.lang.OutOfMemoryError: Bitmap Size Exceeds VM Budget” error. I found this error frecuently on activities using lots of bitmaps after changing orientation: the Activity is destroyed, created again and the layouts are “inflated” from the XML consuming the VM memory avaiable for bitmaps.
Bitmaps on the previous activity layout are not properly deallocated by the garbage collector because they have crossed references to their activity. After many experiments I found a quite good solution for this problem.
First, set the “id” attribute on the parent view of your XML layout:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:id="@+id/RootView"
>
...
Then, on the onDestroy() method of your Activity, call the unbindDrawables() method passing a refence to the parent View and then do a System.gc()
@Override
protected void onDestroy() {
super.onDestroy();
unbindDrawables(findViewById(R.id.RootView));
System.gc();
}
private void unbindDrawables(View view) {
if (view.getBackground() != null) {
view.getBackground().setCallback(null);
}
if (view instanceof ViewGroup) {
for (int i = 0; i < ((ViewGroup) view).getChildCount(); i++) {
unbindDrawables(((ViewGroup) view).getChildAt(i));
}
((ViewGroup) view).removeAllViews();
}
}
This unbindDrawables() method explores the view tree recursively and:
Get the Eclipse Memory Analyzer ( http://www.eclipse.org/mat/) Check http://kohlerm.blogspot.com/2010/02/android-memory-usage-analysis-slides.html and http://kohlerm.blogspot.com/search/label/memory
Mostly for Google travelers from the future:
Most java tools are unfortunately unsuitable for this task, because they only analyze the JVM-Heap. Every Android Application also has a native heap, though, which also has to fit within the ~16 MB limit. It's usually used for bitmap data, for example. So you can run quite easily into Out Of Memory errors even though your JVM-Heap is chillin around 3 MBs, if you use a lot of drawables.
The answer from @hp.android works well if you are just working with bitmap backgrounds but, in my case, I had a BaseAdapter
providing a set of ImageView
s for a GridView
. I modified the unbindDrawables()
method as advised so that the condition is:
if (view instanceof ViewGroup && !(view instanceof AdapterView)) {
...
}
but the problem then is that the recursive method never processes the children of the AdapterView
. To address this, I instead did the following:
if (view instanceof ViewGroup) {
ViewGroup viewGroup = (ViewGroup) view;
for (int i = 0; i < viewGroup.getChildCount(); i++)
unbindDrawables(viewGroup.getChildAt(i));
if (!(view instanceof AdapterView))
viewGroup.removeAllViews();
}
so that the children of the AdapterView
are still processed -- the method just doesn't attempt to remove all children (which is unsupported).
This doesn't quite fix the problem however since ImageView
s manage a bitmap that is not their background. I therefore added the following. It's not ideal but it works:
if (view instanceof ImageView) {
ImageView imageView = (ImageView) view;
imageView.setImageBitmap(null);
}
Overall the unbindDrawables()
method is then:
private void unbindDrawables(View view) {
if (view.getBackground() != null)
view.getBackground().setCallback(null);
if (view instanceof ImageView) {
ImageView imageView = (ImageView) view;
imageView.setImageBitmap(null);
} else if (view instanceof ViewGroup) {
ViewGroup viewGroup = (ViewGroup) view;
for (int i = 0; i < viewGroup.getChildCount(); i++)
unbindDrawables(viewGroup.getChildAt(i));
if (!(view instanceof AdapterView))
viewGroup.removeAllViews();
}
}
I'm hoping there is a more principled approach to freeing up such resources.
Good Google I/O talk (2011) on Memory Management in Android, as well as details on tools + techniques for memory profiling:
http://www.youtube.com/watch?v=_CruQY55HOk
Valgrind has been ported to Android (sponsored by Mozilla). See Valgrind on Android — Current Status and Support Running Valgrind for Android on ARM (comment 67).
Well, those are the tools that hook with the unique formats that Android uses..I think what you may be unsatisfied with is the underlying testing code framework in use..
Have you tried mock testing areas of code using the Android Mock Framework?
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