Android内存管理(2)HUNTING YOUR LEAKS: MEMORY MANAGEMENT IN ANDROID PART 2
from:
http://www.raizlabs.com/dev/2014/04/hunting-your-leaks-memory-management-in-android-part-2-of-2/
HUNTING YOUR LEAKS: MEMORY MANAGEMENT IN ANDROID (PART 2 OF 2)
Posted APRIL 9, 2014 by JOE MAHON
Woo-hoo! Now you know what is happening with your app’s memory usage when you see one of those OOM exception. But, you don’t know where to find the source. Let alone, how to fix it.
Tracking down memory issues can be a pain in the neck. Here are a few strategies that will help you as you pursue the noble craft of Android-igami.
Sample project
There are code snippets and screenshots of what I’m working on sprinkled throughout this post. These will come from a single sample project, and if you prefer to see things in their entirety before being divided into little pieces, I provided that sample project to download here.
- Yes, it uses Eclipse. Sorry to all you cutting edge folks working with the still beta Android Studio.
- Structure: One activity, with a fragment. You’ll see an image that turns on and off every 5 seconds.
- We’re using a singleton manager to keep time for us, and notify the Fragment when it’s time to switch the image on or off.
For those keeping score at home, this project has one A-level memory leak: the type of leak that no developer should ever allow into their code. And I’m not going to tell you where it is. But, that’s the purpose of this experiment! So let’s take a look at where it goes wrong.
EXAMINATION 1: “UPDATE HEAP” BUTTON

- Open the DDMS1 Perspective in Eclipse (It should be found somewhere around the
Window->Open Perspective->Otherpopup dialog). - Highlight the process you wish to profile (most real devices will only show “debug”-signed processes, so you’d only see it on a real device if you built and installed from
adb). - Tap the little green “disk” icon (circled in above image), named “Update Heap.”
- Make sure you open the Heap panel on the right side of the screen.
- Tap the “Cause GC” button2.
This will put some numbers in that chart. The one we should focus on is “Allocated,” which shows you how much memory the Dalvik VM currently has given your app. This can stretch and shrink a bit, but there’s an upper limit (the size of which depends on the device). If you exceed the upper limit, you pop out an OOM and the app crashes.
This is a great tool to keep an eye on while you develop, since it shows a live snapshot of the system after any garbage collection cycle. If you ever notice the allocated memory gradually increasing without ever letting anything go, that’s a good indication that you might have a memory leak.3
EXAMINATION 2: MAT & HPROF — A.K.A., “THE HUNT”
There are some acronyms for you, huh?
“MAT,” the Eclipse Memory Analyzer tool, can read a file that your virtual machine generates (Remember that one we talked about in the last post? It’s called Dalvik). That file, called a “heap dump,” represents the set of all objects currently stored on your process’s heap4. That means you can actually poke around the metadata of the objects you’re using during runtime.
What you’ll need:
- Eclipse (the Google provided “ADT” version works well here).
- MAT.
- If you’re not using the DDMS plugin, you’ll need to manually convert the Dalvik profile into an
.hproffile.
Plot a course
Run the MemoryLeek project on an emulator5. You should see a white screen with a slowly blinking image. Rotate the emulator ten or so times (either 7 on the numpad or Cntrl+F12). Now, return to Eclipse, and open the DDMS perspective once again. This time, we’re aiming for the green disc icon with an arrow attached — indicating you want to generate a heap dump. Go ahead and click it, and you should see a statement in the Android log that looks like:
I/dalvikvm(): hprof: dumping heap strings to "[DDMS]".
That’s good. Now leave it for a bit, because it could take a couple of minutes to generate (and the emulator is frozen until it finishes).
Once the output is ready, it should prompt you about some different types of reports. (If you instead see a prompt to save a file, go ahead and save it somewhere, then use the SDK tool hprof-conv to convert it to the appropriate format, and manually open it with Eclipse’s MAT). Once you have the “reports” prompt, just cancel out of it — we’re not going to use any pre-set reports for this.
Load your ammo
Hooray! If you’re reading this, you’re hopefully looking at some kind of graph of your app’s current memory state. In my case, a pie chart.

Now what?
Let’s try poking around a bit. Click the little bar chart icon in the top left, that says “Create a histogram…” when you hover over it. This will show you a list of all the objects currently allocated, in no particular order.
You’re looking at the name and package of the objects in the first column, the number of that kind of object in existence at the moment in the next column and then some representation of the total heap sizes in the third and fourth columns6.
It might be interesting to sort by size or count to see where the majority of memory is being used in your code7. If you right-click on one of these listings, you can get some cool options, like all instances of objects of that type with all the references that keep it from being garbage collected.

But wait, we’re supposed to be hunting for something, right? We need a target of some kind.
Tracking the game
There’s a lot of noise in an app’s memory space. Fortunately, we can narrow it down in a few ways.
First, try applying filtering to this list. In the first row, you can enter text. Since you almost always want to take a closer look at your code, I suggest entering something like “com.example.memoryleek”. This should filter down to a handful of entries.

Gazing upon these, I’m hoping you notice something horrifying. If you don’t, I recommend taking another look at Part 1 of this series. Hopefully you will figure out what the worst thing in the world to leak would be.
Hint: it’s Activities.
And, good Lord, I have 24 instances of LeekActivity right now! That’s totally not OK.
Why might this be?
Well, kindly, MAT has a way to tell us. You can right-click that list item, and select “Merge Shortest Paths to GC Roots”, excluding WeakReferences (remember, WeakReferences will have no effect on what is retained). This means we want to see what memory root all these objects have in common. That should indicate if there’s something retaining all of them at once, ergo, a leak of some kind!

In this case, there are 3 roots. Two of them are external to this project, so aren’t likely culprits. But, that LeekManager instance looks suspicious….
A-Ha! If you open it up, you can see that there’s a LinkedList called “listeners” on LeekManager that has a reference to all of those Activities.
But, where did that come from?
Well, check this out:
// In LeekFragment.class: @Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); // Add this fragment as a leek listener so
// it can update its view accordingly
LeekManager.get().addListener(this);
}
Every time our LeekFragment is created, we add it as a LeekListener to the LeekManagersingleton. But, when we did those rotations, the Fragment was never removed as a listener. The Fragment has a reference to the parent Activity, and the parent LeekActivity has references to all the other view elements.
Taking the shot
There’s our leak. Fixing it? Easy: LeekFragment needs to be removed from LeekManagerupon its onDestroy() lifecycle method.
Now, the above code looks like:
// In LeekFragment.class: @Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); // Add this fragment as a leek listener so
// it can update its view accordingly
LeekManager.get().addListener(this);
} @Override
public void onDestroy() {
super.onDestroy(); LeekManager.get().removeListener(this);
}
And when we run the app, we don’t see any more indications of leaky memory! Hoorah!
THINGS THAT ARE TRYING TO SNEAK INTO THIS POST:
- Options for unit testing. If anyone has useful memory analysis-based unit test strategies, feel free to shout at me @onemahon. Props to Reddit user
stepwise_refinementfor thegreat question. - Static vs. non-static inner classes. Non-static inner classes keep implicit references to their enclosing class. Be careful when deciding on the
staticmodifier for inner classes, as such. - Some strategies for testing bad memory management during QA phases:
- Lots of rotations.
- Kill the app at unfortunate times (i.e. during activity loads).
- Leave the app open for long periods of time, especially if you have some depth to your activity stack.
- Run the app on an emulator with super small RAM.
PARTING SHOTS
- Never leak an Activity.
- Keep an eye on the heap while the app is running.
- Profile the app’s memory usage every so often with MAT.
Now, you’re ready! Go forth, and leak not!
RESOURCES
Memory Analysis for Android Applications
Android内存管理(2)HUNTING YOUR LEAKS: MEMORY MANAGEMENT IN ANDROID PART 2的更多相关文章
- Android内存管理机制之一:low memory killer
转载自http://www.miui.com/thread-29268-1-1.html 准备写这个专题之前,心里是有点忐忑的.首先Android内存管理机制相当复杂,想要讲清楚比较困难:其次对于绝大 ...
- [Android Memory] Android内存管理、监测剖析
转载自:http://blog.csdn.net/anlegor/article/details/23398785 Android内存管理机制: Android内存管理主要有:LowMemory Ki ...
- Android内存管理(1)WRANGLING DALVIK: MEMORY MANAGEMENT IN ANDROID PART 1
from : http://www.raizlabs.com/dev/2014/03/wrangling-dalvik-memory-management-in-android-part-1-of-2 ...
- Android 内存管理之优化建议
OOM(OutOfMemory)转:http://hukai.me/android-performance-oom/ 前面我们提到过使用getMemoryClass()的方法可以得到Dalvik He ...
- Android——内存管理基础
内存收集概念 内存垃圾收集器(garbage collector) 概念:自定内存管理. 功能:分配内存.保证所有被引用的对象还在内存中.可以释放在运行的代码中不再引用的对象的内存. 垃圾收集器避免了 ...
- Android 内存管理分析(四)
尊重原创作者,转载请注明出处: http://blog.csdn.net/gemmem/article/details/8920039 最近在网上看了不少Android内存管理方面的博文,但是文章大多 ...
- 浅谈Android内存管理
最近在网上看了不少Android内存管理方面的博文,但是文章大多都是就单个方面去介绍内存管理,没有能全局把握,缺乏系统性阐述,而且有些观点有误,仅仅知道这些,还是无法从整体上理解内存管理,对培养系统优 ...
- 移动端测试===Android内存管理: 理解App的PSS
Android内存管理: 理解App的PSS 原文链接:http://www.littleeye.co/blog/2013/06/11/android-memory-management-unders ...
- Android内存管理(4)*官方教程 含「高效内存的16条策略」 Managing Your App's Memory
Managing Your App's Memory In this document How Android Manages Memory Sharing Memory Allocating and ...
随机推荐
- 【BZOJ】【1503】 【NOI2004】郁闷的出纳员
Splay Splay的模板题吧……妥妥的序列操作= =(好像有段时间没写过这种纯数据结构题了……) /************************************************ ...
- 【BZOJ】【2631】Tree
LCT 又一道名字叫做Tree的题目…… 看到删边加边什么的……又是动态树问题……果断再次搬出LCT. 这题比起上道[3282]tree的难点在于需要像线段树维护区间那样,进行树上路径的权值修改&am ...
- IIS网站打不开错误解决方案集锦(一):编译器错误消息: 编译器失败,错误代码为 -1073741502。
[2015-05-12解决记录] 问题:服务器上的文件一直都是好的,但是运行了很长一段时间以后,发现网站打不开,或者上传不了图片了,怎么办? 错误信息:c:\windows\system32\inet ...
- GS连接事件
GS网络连接事件 //网络事件 //这个事件是在libevent里面的收到的事件就是在那个listen里面,就是客户端打开,服务器收到通知 link_stat stat = (link_stat)rP ...
- [REFERENCE] Real-Time-Normal-Map-Dxt-Compression
DXT5N & 3Dc(aka BC5) compression in common code & SIMD: http://mrelusive.com/publications/pa ...
- 简单CSS hack:区分IE6、IE7、IE8、Firefox、Opera
一.跨浏览器的网页设计一直是让人很头疼的问题,这不只是因为浏览器的版本众多,还有一个重要的原因是相同浏览器的不同时期的版本也会有差异,甚至是在不同操作同台上还会有不同.因此使CSS hack技术进行浏 ...
- Sqli-labs less 20
Less-20 从源代码中我们可以看到cookie从username中获得值后,当再次刷新时,会从cookie中读取username,然后进行查询. 登录成功后,我们修改cookie,再次刷新时,这时 ...
- 利用dsniff的tcpkill杀TCP连接
利用dsniff的tcpkill杀TCP连接 Linux连接久久不能释放的现象不常见,但偶然也会发生.进程虽不复存在,但是客户端的连接咬定青山不放松,死活也不肯吐出连接,导致重启进程时因操作系统判断监 ...
- 最长连续回文串(最优线性时间O(n))
转自:http://blog.csdn.net/hopeztm/article/details/7932245 Given a string S, find the longest palindrom ...
- iOS验证码倒计时(GCD实现)
+ (void)verificationCode:(void(^)())blockYes blockNo:(void(^)(id time))blockNo { __block ; //倒计时时间 d ...