HUNTING YOUR LEAKS: MEMORY MANAGEMENT IN ANDROID

转:http://www.raizlabs.com/dev/2014/04/hunting-your-leaks-memory-management-in-android-part-2-of-2/



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

memory-1

  1. Open the DDMS1 Perspective in Eclipse (It should be found somewhere around the Window->Open Perspective->Other popup dialog).
  2. 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).
  3. Tap the little green “disk” icon (circled in above image), named “Update Heap.”
  4. Make sure you open the Heap panel on the right side of the screen.
  5. 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 .hprof file.
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:

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.

memory-2

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.

memory-3

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.

memory-4

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!

memory-5

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:

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 LeekManager upon itsonDestroy() lifecycle method.

Now, the above code looks like:

 

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_refinement for the great question.
  • Static vs. non-static inner classes. Non-static inner classes keep implicit references to their enclosing class. Be careful when deciding on the static modifier 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

How to Leak a Context: Handlers & Inner Classes

Investigating Your RAM Usage

Eclipse Memory Analyzer Help


  1. The Dalvik Debug Monitor Server↩
  2. “Cause GC” references the GarbageCollector, which was discussed in the previous post. This button does essentially the same thing as calling System.gc() from code, but with a slightly different log output. If you’re ever curious about all the dalvikvm-tagged Logcat items, take a look at this documentation.↩
  3. The Dalvik VM expands your heap as you need more space, until it reaches an upper limit set by the system. Be careful while analyzing the size of the heap, as “allocated”, “free”, and “% used” can fluctuate until your max heap size is reached.↩
  4. Dalvik doesn’t actually use the hprof format, because it’s a unique JVM. However, it uses something similar, and the Android SDK comes with a command-line tool called hprof-conv, which can transform the Dalvik-generating profile into the MAT-readable profile.↩
  5. In fact, it’s more trouble than it’s worth to try this on real devices. The heap dump will take forever to transfer via USB, and on some devices, it will actually overflow and fail before it can finish, since the heap can get massive on modern phones.↩
  6. “Shallow Heap” represents the total size of all of those objects and their direct member data (i.e., all the Strings and Lists they might have direct reference to). “Retained Heap” is a little more interesting: it’s the amount of memory that could potentially be freed during the next garbage collection cycle, if those objects no longer had any references. In other words, it’s the sum of the objects whose memory origin is this type of object.↩
  7. It’s worth noting that not all the top-sized memory entries will make immediate sense. For instance, image data is held in byte arrays, but not differentiated in any meaningful way.↩

2 THOUGHTS ON “HUNTING YOUR LEAKS: MEMORY MANAGEMENT IN ANDROID (PART 2 OF 2)”

  1. Pingback: Wrangling Dalvik: Memory Management in Android (Part 1 of 2) | Dev Blog

  2. This is a great resource! Thanks for taking the time to put this together. This was a tipping point in helping me understand the capabilities of the MAT tool.


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
城市应急指挥系统是智慧城市建设的重要组成部分,旨在提高城市对突发事件的预防和处置能力。系统背景源于自然灾害和事故灾难频发,如汶川地震和日本大地震等,这些事件造成了巨大的人员伤亡和财产损失。随着城市化进程的加快,应急信息化建设面临信息资源分散、管理标准不统一等问题,需要通过统筹管理和技术创新来解决。 系统的设计思路是通过先进的技术手段,如物联网、射频识别、卫星定位等,构建一个具有强大信息感知和通信能力的网络和平台。这将促进不同部门和层次之间的信息共享、交流和整合,提高城市资源的利用效率,满足城市对各种信息的获取和使用需求。在“十二五”期间,应急信息化工作将依托这些技术,实现动态监控、风险管理、预警以及统一指挥调度。 应急指挥系统的建设目标是实现快速有效的应对各种突发事件,保障人民生命财产安全,减少社会危害和经济损失。系统将包括预测预警、模拟演练、辅助决策、态势分析等功能,以及应急值守、预案管理、GIS应用等基本应用。此外,还包括支撑平台的建设,如接警中心、视频会议、统一通信等基础设施。 系统的实施将涉及到应急网络建设、应急指挥、视频监控、卫星通信等多个方面。通过高度集成的系统,建立统一的信息接收和处理平台,实现多渠道接入和融合指挥调度。此外,还包括应急指挥中心基础平台建设、固定和移动应急指挥通信系统建设,以及应急队伍建设,确保能够迅速响应并有效处置各类突发事件。 项目的意义在于,它不仅是提升灾害监测预报水平和预警能力的重要科技支撑,也是实现预防和减轻重大灾害和事故损失的关键。通过实施城市应急指挥系统,可以加强社会管理和公共服务,构建和谐社会,为打造平安城市提供坚实的基础。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值