Placeholder Image

字幕列表 影片播放

  • PATRICK DUBROY: Hi everybody, my name's Patrick Dubroy and

  • today I'm going to talk to you about memory

  • management for Android.

  • So I'm really happy to see so many people here who care

  • about memory management, especially near

  • the end of the day.

  • So let's get started.

  • So I'm sure you all remember this device.

  • This is the T-Mobile G1.

  • Hugo talked about it in the keynote yesterday.

  • It was released about two and a half years ago.

  • So, is there anybody here who actually developed on the G1?

  • All right, impressive.

  • That's about maybe 40% of the room.

  • So you may remember that the G1 came with 192

  • megabytes of RAM.

  • And in fact, most of that was used up by the system.

  • There wasn't a whole lot left over for apps.

  • Fast forward a few years later, we have the Motorola

  • Zoom released just a couple of months ago.

  • The Zoom comes with a gigabyte of RAM.

  • Now some people might hear this and think, OK, my

  • troubles are over.

  • I never have to worry about memory management again.

  • And of course, given that we have a whole room here, you

  • guys are all smart people and you realize

  • that that's not true.

  • And there are a couple of reasons for this.

  • First of all, the Zoom has six and a half times the

  • resolution that the G1 had.

  • So you've got six and a half times as

  • many pixels on screen.

  • That means you're probably going to

  • need to use more memory.

  • You got more bitmaps for example.

  • The other thing is that on tablets, you really want to

  • create a new kind of application.

  • You know, the rich, immersive applications, like this

  • YouTube app, for example.

  • These are going to take a lot of memory.

  • There's tons of bitmaps in here.

  • Those use up a lot of memory.

  • Also, the Zoom we're talking about a pretty new device.

  • This is basically bleeding edge hardware.

  • Most your customers are not going to be using something

  • that's only two months old.

  • So of course, you want to support people who are using

  • older hardware as well.

  • Finally, maybe you're all familiar with Parkinson's Law,

  • which says that work always take as much time as you have.

  • And really, it's kind of the same for software.

  • So, no matter how much memory you have, you're going to find

  • a way to use it and wish you had just a little bit more.

  • What I want to talk about today are basically two

  • different things.

  • First of all, I want to cover some of the changes that we've

  • made in Gingerbread and Honeycomb that affect how your

  • app uses memory.

  • That's your cameo.

  • All right, so as I was saying, there are two different things

  • I want to cover today.

  • So first of all, I want to talk about some of the changes

  • we've made in Gingerbread and Honeycomb that affect how your

  • apps use memory.

  • And basically, memory management in general.

  • In the second half of the talk I want to talk about some

  • tools you can use to better understand how your app is

  • using memory.

  • And if you have memory leaks, how you can figure out where

  • those memory leaks are.

  • So just to set expectations for this talk, I'm going to

  • make them some assumptions about the stuff you've done

  • and it'll really help you get the most out of this if you're

  • familiar with these concepts.

  • So I'm hoping that you've all written Android apps before.

  • And it looked like about half the room had developed on the

  • G1, so that's probably true.

  • I hope that most of you have heard of the Dalvik heap.

  • You have a basic idea of what I'm talking about when I talk

  • about heap memory.

  • I'm sure you're familiar with the garbage collector.

  • You have a basic idea hopefully of what garbage

  • collection is and how it works.

  • And probably, most of you have seen an OutOfMememoryError

  • before and you have a basic idea of why you get it and

  • what you can do to deal with it.

  • So first, let's talk about heap size.

  • So you may know that in Android, there's a hard limit

  • on your application's heap size.

  • And there's a couple reasons for this.

  • So first of all, one of the great features of Android is

  • that it has full multitasking.

  • So you actually have multiple programs running at once.

  • And so obviously, each one can't use all of

  • your devices memory.

  • We also don't want a runaway app to just start getting

  • bigger and bigger and bloating the entire system.

  • You always want your dialer to work, your launcher t work,

  • that sort of thing.

  • So there's this hard limit on heap size and if your

  • application needs to allocate more memory and you've gone up

  • to that heap size limit already, then you're basically

  • going to get an out of memory error.

  • So this heap size limit is device dependent.

  • It's changed a lot over the years.

  • On the G1 it was 16 megabytes.

  • On the Zoom it's now 48 megabytes.

  • So it's a little bit bigger.

  • If you're writing an app and you want to know, OK, how much

  • heap space do I have available?

  • You know, maybe you want to decide how much stuff to keep

  • in a cache for example.

  • There's a method you can use in ActivityManager,

  • getMemoryClass that will return an integer value in

  • megabytes, which is your heap size.

  • Now these limits were designed assuming that you know almost

  • any app that you would want to build on Android should be

  • able to fit under these limits.

  • Of course, there are some apps that are

  • really memory intensive.

  • And as I said, on the tablet, we really want to build almost

  • a new class of application.

  • It's quite a different than the kind of things you were

  • building on phones.

  • So we thought, if someone wants to build an image

  • editor, for example, on the Zoom, they should

  • be able to do that.

  • But an image editor's a really memory intensive application.

  • It's unlikely that you could build a good one that used

  • less than 48 megabytes of heap.

  • So in Honeycomb we've added a new option that allows

  • applications like this to get a larger heap size.

  • Basically, , you can put something in your

  • AndroidManifest, largeHeap equals true.

  • And that will allow your application to use more heap.

  • And similarly, there's a method you can use to

  • determine how much memory you have available to you.

  • The ActivityManager getLargeMemoryClass method

  • again, will return an integer value of this large heap size.

  • Now before we go any further, I want to give

  • a big warning here.

  • You know, this is not something you should be doing

  • just because you got an out of memory error, or you think

  • that your app deserves a bigger heap.

  • You're not going to be doing yourself any favors because

  • your app is going to perform more poorly because bigger

  • heap means you're going to spend more time at garbage

  • collection.

  • Also, your users are probably going to notice because all

  • their other apps are getting kicked out of memory.

  • It's really something you want to reserve for when you really

  • understand OK, I'm using tons of memory and I know exactly

  • why I'm using that memory, and I really

  • need to use that memory.

  • That's the only time that you should be using this large

  • heap option.

  • So I mentioned garbage collection.

  • And that it takes longer when you have a bigger heap.

  • Let's talk a little bit about garbage collection.

  • So I just want to go through a quick explanation here of what

  • [INAUDIBLE]

  • garbage collection is doing.

  • So basically, you have a set of objects.

  • First of all, let's say these blue objects here, these are

  • the objects in your heap.

  • And they form a kind of graph.

  • They've got references to each other.

  • Some of those objects are alive, some of them are not

  • used anymore.

  • So what the GC does is it starts from a set of objects

  • that we call the roots.

  • These are the objects that the GC knows is alive.

  • For example, variables that are alive on a thread stack, J

  • and I global references, we treat objects in the zygote as

  • heap, or as roots as well.

  • So basically, the GC starts with those objects and starts

  • visiting the other objects.

  • And basically, traversing through the whole graph to

  • find out which objects are referenced directly or

  • indirectly from the GC roots.

  • At the end of this process, you've got some objects left

  • over, which the GC never visited.

  • Those are your garbage.

  • They can be collected.

  • So it's a pretty simple concept.

  • And you can see why when I said that you have bigger

  • heaps you're going to have larger pause times.

  • Because the garbage collector basically has to traverse your

  • entire live set of objects.

  • If you're using say the large heap option and you've got 256

  • megs of heap, well, that's a lot of memory for the garbage

  • collector to walk over.

  • You're going to see longer pause times with that.

  • We have some good news though.

  • In Gingerbread, there have been some great changes to the

  • garbage collector that make things a lot better.

  • So in Gingerbre--

  • sorry, pre-Gingerbread, the state of the garbage collector

  • was that we had to stop the world collector.

  • So what this means is that basically, when a garbage

  • collection is in progress, your application is stopped.

  • All your application threads are completely stopped while

  • the collection is proceeding.

  • This is a pretty standard things.

  • These pauses generally tend to be pretty short.

  • What we found was that pause times as heaps were getting

  • bigger, these were getting to be a little bit too long.

  • So we were seeing stuff up 50 to 100 milliseconds.

  • And if you're trying to build a really responsive app that

  • kind of pause time is not really acceptable.

  • So in Gingerbread, we now have a

  • concurrent garbage collector.

  • It does most of its work concurrently, which means that

  • your application is not stopped for the duration of

  • the garbage collection.

  • Basically, we have another thread that's running at the

  • same time as your application that can perform garbage

  • collection work.

  • You'll see basically two short pauses.

  • One at the beginning of a collection and

  • one near the end.

  • But these pause times are going to be much, much lower.

  • Usually you'll see two, three, four, or five milliseconds for

  • your pause time.

  • So that's a significant improvement.

  • Pause times about 10% of what they used to be.

  • So that's a really good change that we have in Gingerbread.

  • Now if you're building memory heavy apps, there's a good

  • chance you're using a lot of bitmaps.

  • We found that in a lot of apps you have maybe 50 or 75% of

  • your heap is taken up by bitmaps.

  • And in Honeycomb because you're going to be developing

  • on tablets, this gets even worse.

  • Because your images are bigger to fill the screen.

  • So before Honeycomb, the way we managed bitmaps was this.

  • So the blue area up here is the Dalvik heap and this

  • yellow object is a bitmap object.

  • Now bitmap objects are always the same size in the heap no

  • matter what their resolution is.

  • The backing memory for the bitmap is actually stored in

  • another object.

  • So the pixel data is stored separately.

  • Now before Honeycomb what we did was this pixel data was

  • actually native memory.

  • It was allocated using malloc outside the Dalvik heap.

  • And this had a few consequences.

  • If you wanted to free this memory you could either call

  • recycle, which would free the memory synchronously.

  • But if you didn't call recycle and you were waiting for your

  • bitmap to get garbage collected, we had to rely on

  • the finalizer to free the backing memory for the bitmap.

  • And if you're familiar with finalization, you probably

  • know that it's an inherently unreliable process.

  • Just by its nature it takes several collections, usually

  • for finalization to complete.

  • So this can cause problems with bitmap heavy app as you

  • had to wait for several garbage collections before

  • your pixel data was reclaimed.

  • And this could be a lot of memory because bitmap pixel

  • data is quite a significant portion of the heap.

  • This also made things harder to debug.

  • If you were using standard memory analysis tools like the

  • Eclipse Memory Analyzer, it couldn't actually see this

  • native memory.

  • You would see this tiny bitmap object.

  • Sure, but that doesn't tell you very much.

  • You don't mind if you have a 10 by 10 bitmap.

  • But if you have a 512 by 512 bitmap it's a big difference.

  • Finally, the other problem that we had with this approach

  • was that it required full stop the world garbage collections

  • in order to reclaim the backing memory, assuming that

  • you didn't call recycle, that is.

  • The good news is in Honeycomb we've changed

  • the way this works.

  • And the bitmap pixel data is now allocated inside the

  • Dalvik heap.

  • So this means it can be freed synchronously by the GC on the

  • same cycle that your bitmap gets collected.

  • It's also easier to debug because you can see this

  • backing memory in standard analysis tools like Eclipse

  • Memory Analyzer.

  • And I'm going to do a demo in a few minutes and you'll see

  • really, how much more useful this is when you

  • can see that memory.

  • Finally, this strategy is more amenable to concurrent and

  • partial garbage collections, which means we can generally

  • keep those pause times down.

  • So those are the two biggest changes that we've introduced

  • in Gingerbread and Honeycomb that affect how

  • your apps use memory.

  • And now I want to dive in to some tools that you can use to

  • better understand how much memory your app's using.

  • And if you have memory leaks, better understanding where

  • those leaks are and generally, how your app is using memory.

  • The most basic tool you can use for understanding your

  • apps memory usage is to look at your log messages.

  • So these are the log messages that you see in DDMS in the

  • log cat view.

  • You can also see them at the command line

  • using ADB log cat.

  • And every time a garbage collection happens in your

  • process, you're going to see a message that looks something

  • like this one.

  • And I just want to go through the different parts of this

  • message, so you can better understand what

  • it's telling you.

  • The first thing we have is the reason for the garbage

  • collection.

  • Kind of what triggered it and what kind of collection is it.

  • This one here was a concurrent collection.

  • So a concurrent collection is triggered by basically, as

  • your heap starts to fill up, we kick off our concurrent

  • garbage collection so that it can hopefully complete before

  • your heap gets full.

  • Other kinds of collections that you'll

  • see in the log messages.

  • GC for malloc is one of them.

  • That's what happens when say, we didn't complete the

  • concurrent collection in time and your application had to

  • allocate more memory.

  • The heap was full, so we had to stop and do a garbage

  • collection.

  • You'll see GC external alloc, which is for externally

  • allocated memory, like the bitmap pixel

  • data which I mentioned.

  • It's also used for NIO direct byte buffers.

  • Now this external memory as I mentioned, has

  • gone away in Honeycomb.

  • Basically everything is allocated inside

  • the Dalvik heap now.

  • So you won't see this in your log messages in

  • Honeycomb and later.

  • You'll also see a message if you do an HPROF, if you create

  • an HPROF profile.

  • And finally, the last one I want to

  • mention is GC explicit.

  • You'll see this generally when you're calling system.gc,

  • which is something that you know you really

  • should avoid doing.

  • In general, you should trust in the garbage collector.

  • We've got some information also about the amount of

  • memory that was freed on this collection.

  • There's some statistics about the heap.

  • So the heap in this case, was 65% free after

  • the collection completed.

  • There's about three and a half megs of live objects and the

  • total heap size here is listed as well.

  • It's almost 10 megs, 9,991 K.

  • There's some information about externally allocated memory,

  • which is the bitmap pixel data and also,

  • NIO direct byte buffers.

  • The two numbers here, the first number is the amount of

  • external memory that your app has allocated.

  • The second number is a sort of soft limit.

  • When you've allocated that much memory, we're going to

  • kick off a GC.

  • Finally, you'll see the pause times for that collection.

  • And this is where you're going to see the effect

  • of your heap size.

  • Larger heaps are going to have larger pause times.

  • The good news is for a concurrent collection, you're

  • going to see these pause times generally pretty low.

  • Concurrent collections are going to show two pause times.

  • There's one short pause at the beginning of the collection

  • and one most of the way through.

  • Non-concurrent collections you'll see a single pause

  • time, and this is generally going to

  • be quite a bit higher.

  • So looking at your log messages is a really basic way

  • to understand how much memory your app is using.

  • But it doesn't really tell you, where am

  • I using that memory?

  • What objects are using this memory?

  • And the best way to do that is using heap dumps.

  • So a heap dump is basically a binary file that contains

  • information about all of the objects in your heap.

  • You can create a heap dump using DDMS by clicking on the

  • icon, this somewhat cryptic icon.

  • I think [INAUDIBLE]

  • mentioned it in the previous talk.

  • There's also an API for creating heap dumps.

  • In general, I find using DDMS is fine.

  • There are times when you want to create a heap dump at a

  • very, very specific point in time.

  • Maybe when you're trying to track down a memory leak.

  • So it can be helpful to use that API.

  • You may need to convert the heap dump to the standard

  • HPROF format.

  • You'll only need to do that if you're using the standalone

  • version of DDMS. If you're using the Eclipse plug-in, the

  • ADT plug-in, it will automatically convert it.

  • But the conversion is pretty simple.

  • There's a tool in the Android SDK, which you

  • can use to do it.

  • And after you've converted it to the standard HPROF format,

  • you can analyze it with standard heap analysis tools,

  • like MAT or jhat.

  • And I'm going to show an example of MAT, which is the

  • shorter way of saying the Eclipse Memory Analyzer.

  • And before I jump into the demo, I want to talk about

  • memory leaks.

  • So there's kind of a misconception that in a

  • managed run time, you can't have memory leaks.

  • And I'm sure you guys know that's not true.

  • Having a garbage collector does not prevent memory leaks.

  • A memory leak in a managed run time is a little bit different

  • though, than a memory leak in C or C++.

  • Basically, a leak is when you have a reference to an unused

  • object that's preventing that object from

  • being garbage collected.

  • And sometimes you can have a reference to a single object,

  • but that object points to a bunch of other objects.

  • And basically, that single reference is preventing a

  • large group of objects from being collected.

  • One thing to watch out for in Android.

  • I see people sometimes and I've done this myself,

  • accidentally create a memory leak by holding a long lived

  • reference to an activity.

  • So you need to be really careful with that and maybe

  • it's you're holding a reference to the context and

  • that's what happens.

  • You can also do it by keeping a long lived reference to a

  • view or to a drawable, because these will also hold a

  • reference to the activity that they were originally in.

  • And the reason that this is a problem, the reason this

  • causes a memory leak is this.

  • So you've got your activity, it contains a view group, a

  • linear layout or something, and it contains some views.

  • And we've got a reference from the framework to the currently

  • visible activity.

  • But in Android, when you have a rotation event, so you

  • rotate your device, what we do is actually build up a new

  • view hierarchy because you need to load new resources,

  • you may have a brand new layout for landscape or

  • portrait, you may have differently

  • sized icons or bitmaps.

  • And then we basically remove the reference to the old view

  • hierarchy and point to the new one.

  • And the idea is that this old view hierarchy sure get

  • garbage collected.

  • But if you're holding a reference to that, you're

  • going to prevent it from getting garbage collected.

  • And that's why it's a problem to hold the long lived

  • reference to an activity or even to a view because in

  • fact, the arrows connecting these objects should be going

  • in both directions.

  • Because you've got pointers all the way up.

  • So if you do have a memory leak, a really good way to

  • figure out where it is is using the

  • Eclipse Memory Analyzer.

  • I'm going to do a demo of that, but I want to first

  • cover some of the concepts behind the Memory Analyzer, so

  • that when I do the demo you'll better understand what I'm

  • showing you.

  • So the Eclipse Memory Analyzer can be downloaded from the

  • eclipse.org site.

  • It comes in a couple of flavors.

  • There's an Eclipse plug-in version, there's also a

  • standalone version.

  • I'm going to be demonstrating the standalone version.

  • I just personally prefer not to have Eclipse have all these

  • different plug-ins.

  • I kind of like to keep things a little bit separate.

  • But they're basically the same.

  • Now, Memory Analyzer has some important concepts that

  • you'll see a lot.

  • It talks about shallow heap and retained heap.

  • So the shallow heap of an object is just how large is

  • this object, it's size and bytes.

  • It's really simple.

  • So let's say that all of these objects are 100 bytes.

  • So they're shallow heap is 100 bytes.

  • It's easy.

  • The retained heap is something different.

  • Basically, the retained heap says, if I have an object here

  • and I were to free this object, what other objects is

  • it pointing to?

  • And could those be freed at the same time?

  • And so you calculate the retained heap in terms of,

  • what is the total size of objects that could be freed by

  • freeing this one object?

  • So maybe it's best to understand with an example.

  • So this object down on the right-hand side in yellow,

  • this guy doesn't point to any other objects.

  • So his retained size is pretty easy to calculate.

  • His retained heap is 100.

  • This guy on top, he has a pointer to one other object.

  • But he's not holding that object alive.

  • There are other pointers to that same object.

  • So this guy's retained heap is also just 100 bytes.

  • Because if we were to remove this object, it's not going to

  • free up any other objects.

  • The object down at the end however, it's basically

  • keeping all the other objects alive.

  • So its retained heap is 400 because if we could free that

  • object, we could free all the other objects well, on this

  • slide anyway.

  • So you might be wondering, how do you go about calculating

  • the retain heap?

  • So you're going to see this in Memory Analyzer.

  • And actually, knowing how it calculates the retained heap

  • is quite useful.

  • So the Memory Analyzer uses a concept called

  • the denominator tree.

  • This is a concept from graph theory.

  • Basically, if you have a node A and a node B, A is said to

  • be the dominator of B if every path to B goes through A.

  • And so you might see how that could help us figure out what

  • the retained heap of an object is.

  • So another example here.

  • So let's start with A.

  • It's kind of the root.

  • B and C are only accessible through A.

  • So it's pretty straightforward.

  • They're children of A and the dominator tree.

  • E is also only accessible through C.

  • So it's a child of C in the dominator tree.

  • D is a little bit interesting here.

  • D can be accessed through B or C, but A is on

  • every path to D.

  • So that means that A is the parent of D and

  • the dominator tree.

  • And now you're going to see this dominator tree concept

  • also pop up in Memory Analyzer in its UI.

  • And it can be really helpful for

  • tracking down memory leaks.

  • So let's jump in and do a demo of debugging and

  • memory leak with MAT.

  • So what I'm going to use for this demo is the Honeycomb

  • gallery's sample application.

  • It's a simple application that comes with the Android SDK the

  • basically just demonstrates some of

  • the features of Honeycomb.

  • And really, all it is is a little app the lets you page

  • through some photos.

  • Pretty simple.

  • Now I've done something kind of naughty here.

  • I've introduced a memory leak into this application.

  • And I'll show you how I've done that.

  • Sorry, I better switch to the slides again.

  • So you'll see here I have the source code, an excerpt of the

  • source code from the activity.

  • And so what I've done here is I've introduced this inner

  • class called leaky.

  • And this is not a static inner class.

  • So you may know that if you create a non-static inner

  • class, it actually keeps a reference to

  • the enclosing object.

  • And this is because from a non-static inner class, you

  • can actually refer to the instance variables of the

  • enclosing object.

  • So it's going to retain a reference to

  • the activity here.

  • That's fine as long as this object doesn't live longer

  • than the activity.

  • But I've got this static field and statics live longer than

  • any particular instance.

  • And in my on create method, what I've done is instantiated

  • the leaky object and stored it into the static field.

  • So if you want to be able to visualize this, I basically

  • got my view hierarchy that starts with the main activity.

  • I've instantiated this leaky object and he has a reference

  • to the main activity because that was its enclosing class.

  • Finally, I have the main activity class, which is

  • conceptually a different area of memory than

  • any particular instance.

  • And there's a static variable pointing to the leaky object.

  • So maybe you can see how this is going to cause a memory

  • leak when I rotate the screen.

  • So let's jump in and take a look at this memory leak.

  • So if you want to figure out whether you have a memory

  • leak, one of the easiest ways is to just kind of look at

  • your log messages.

  • So I'm just going to do that.

  • I'm going to do it at the command line.

  • I can just type ADB log cat.

  • And I want to restrict it to the particular process that

  • I've got running here.

  • I don't want to see all of the log messages on the system.

  • So I'm just going to grab on the process ID.

  • There we see a bunch a log messages, including some

  • garbage collection messages.

  • And the number you want to look at is basically the first

  • number here in the 9805K.

  • The first number in your heap size.

  • This is the amount of live objects in the system.

  • And if you're looking for a memory leak, that's what you

  • want to look at.

  • So I'm going to flip through some of the photos here.

  • And you'll see that that number stays pretty constant.

  • We're up to 9872.

  • But basically, the heap usage is pretty constant.

  • Now when I rotate this device, we're going to be a bunch of

  • garbage collections happen.

  • That heap usage goes up and it doesn't go down again.

  • So we're now up to 12 megs of heap.

  • So we leaked about two and a half megs.

  • So whenever you see your memory go up in kind of a step

  • function like that, it steps up and just never goes back

  • down, that's a good sign you have a memory leak.

  • So once you know that you have a leak, what you'll want to do

  • is create a heap dump, so you can go about debugging it.

  • So I'm going to do that right now.

  • I'll open up DDMS.

  • You just need to select the process that you care about

  • and click on this icon up in the toolbar that says dump

  • HPROF file.

  • That'll create a heap dump.

  • It takes a few seconds because it's dumping basically a huge

  • binary file out to disk.

  • And then I can just save it in a file called dump.hprof.

  • And then, because I'm using this standalone version of

  • DDMS here, I need to convert this file.

  • As I mentioned, if you're using the ADT plug-in for

  • Eclipse and using DDMS in there, you don't need to go

  • through this conversion step.

  • But it's really simple.

  • Now that I've converted it, I can open up the Eclipse Memory

  • Analyzer and take a look at this heap dump.

  • So there's not much to see in the Memory Analyzer until

  • you've opened up a heap dump, which we can do just

  • from the file menu.

  • Open heap dump.

  • And I'll open up this converted heap dump, which I

  • just created.

  • Doesn't take very long for it to load up.

  • And the first thing you'll see is this pie chart.

  • This is showing the biggest objects in the system by

  • retained size.

  • Now this alone doesn't really tell us too much.

  • You can see that down in the bottom left here, when I mouse

  • over the various slices of the pie, it's telling me what kind

  • of object I've got.

  • But that doesn't really tell us too much.

  • If we want to get some more information, you want

  • to look down here.

  • There are two views.

  • The histogram view and the dominator tree.

  • And these are the ones that I find most useful and I'm going

  • to show to you.

  • Let's take a look at the dominator tree.

  • You remember the concept I explained.

  • This is how it can be useful in tracking

  • down a memory leak.

  • So what we've got here is basically a list of instances

  • or a list of objects in this system organized.

  • There's a column here.

  • Organized by the amount of retained heap.

  • So when you've got a memory leak, looking at the amount of

  • retained heap is often a good way to look at things because

  • that's going to have the biggest effect on how much

  • memory you're using.

  • And chances are, if you've noticed that you've got a

  • leak, you're leaking a significant amount.

  • So let me just zoom in here.

  • Hopefully you guys can see this a bit better.

  • So at the very top of the list we have the resources class.

  • That's not too surprising because our resources we have

  • to load lots of bitmaps.

  • That's going to hold lots of memory alive.

  • That's fine.

  • These two bitmap objects are interesting.

  • I've got these two large bitmaps, more than two and a

  • half megs each.

  • It's funny because that sounds about like the amount of

  • memory that I was leaking.

  • So if I want to investigate a bit further, I can right click

  • on one of these objects and choose path to GC roots.

  • And I'll chose excluding weak references because I want to

  • see what's keeping that object alive.

  • And a weak reference is not going to keep it alive.

  • So this opened up a new tab and what do you know?

  • It actually points right to my leak.

  • So when you're creating leaks in your application, make sure

  • you name it something really helpful like this so you can

  • find it easily.

  • AUDIENCE: [LAUGHTER]

  • PATRICK DUBROY: So some of you might have noticed this, that

  • if there's only a single path to this object, because that's

  • all I can see here, why didn't this leak object show up in

  • the dominator tree?

  • I mentioned that the dominator tree should show you the

  • largest objects by their amount of retained heap.

  • And well this is a single object that's responsible for

  • retaining the bitmap.

  • So the reason for that is that the Eclipse Memory Analyzer,

  • when it calculates the dominator tree, it actually

  • doesn't treat weak references separately.

  • It basically just treats them like a normal reference.

  • So you'll see that if I actually right click on this

  • guy again and say path to GC roots, and say with all

  • references, then there's actually another

  • path to this object.

  • But it's a weak reference.

  • Generally you don't need to be too concerned about weak

  • references because they're not going to prevent your object

  • from being garbage collected.

  • But that's why the leak object didn't show up in the

  • dominator tree.

  • So the dominator tree is one really, really useful way of

  • tracking down a memory leak.

  • Another thing I like to use is the histogram view.

  • So I mentioned that in Android, it's common to leak

  • memory by keeping long lived references to an activity.

  • So you may want to actually go and look at the number

  • instances of your main activity class that you have.

  • And the histogram view lets you do that.

  • So the histogram view just shows a list of all the

  • classes in its system and right now it's sorted based on

  • the amount of shallow heap occupied by

  • classes in the system.

  • So at the very top there, we see we have byte arrays.

  • And the reason for this is that byte arrays are now the

  • backing memory for pixel data.

  • And you know, this is a perfect example of why it's

  • really useful that we now have the pixel

  • data inside the heap.

  • Because if you're using this on Gingerbread or earlier,

  • you're not going to see byte arrays at the top.

  • Because that memory with allocated in native memory.

  • So we could also, if we were concerned about these byte

  • array objects, we might want to right click on it and say

  • list objects with incoming references.

  • And we've got our two large byte array objects here.

  • We can right click on one and say, path to GC roots,

  • excluding weak references.

  • So this guy looks to have several paths,

  • which keep it alive.

  • Nothing looks out of the ordinary to me.

  • And when you're trying to find a memory leak, there's not

  • really a magic answer for how you find a leak.

  • You really have to understand your system and understand

  • what objects are alive, why they're alive, during the

  • various parts of your application.

  • But you'll see if I look at this other byte array object,

  • and again, do path to GC roots excluding weak references,

  • well, I've found my leak again.

  • So this was another way that I might have found this if it

  • weren't so obvious from the dominator tree.

  • The histogram view can also help us look for

  • our activity instances.

  • So there's a lot of classes obviously in the system.

  • Our activity is not here.

  • There's 2,200 classes.

  • But luckily, Eclipse Memory Analyzer has this handy little

  • filter view at the top.

  • You can just start typing a regular expression.

  • And it'll return you all the classes that match that.

  • So here we've got our main activity.

  • And it tells us that there are actually two instances of this

  • main activity.

  • And that should kind of be a red flag.

  • Normally you should expect to see only a single instance of

  • your main activity alive.

  • Now I mentioned during the screen rotation, we build up a

  • new view hierarchy, there's going to be a brief time where

  • there's two instances alive.

  • But for the most part, you should expect to see one here.

  • So I might think, OK, this is a red flag.

  • Let's take a look.

  • So I can right click on this object and list objects with

  • incoming references.

  • So I want to look at what instances do I have and what's

  • pointing to them?

  • And so I've got two instances here.

  • If I right click on one of them and choose path to GC

  • roots, excluding weak references, I've again, found

  • my memory leak.

  • And in looking at this, I might realize that, oh, I

  • really didn't intend to do this.

  • I didn't mean to keep this reference there.

  • So that's another way that you could have found the leak.

  • So now that we've discovered where our memory leak is, why

  • don't we actually go ahead and fix it.

  • So in this case, the problem was that we had a non-static

  • inner class.

  • So we could fix this by making it a static inner class.

  • And then it wouldn't actually keep a reference to the

  • enclosing activity.

  • The other thing we could do is actually just not store it in

  • a static variable.

  • So it's fine if this leaky object has a reference to the

  • activity, as long as it doesn't live

  • longer than the activity.

  • So let's do that.

  • Let's just make this a regular instance

  • variable and not a static.

  • So then I can go in here recompile this and push it to

  • the device.

  • And hopefully, we should see that our memory leak has been

  • eliminated.

  • Sorry, what we actually want to do is look at our log

  • output in order to see how much memory we're using.

  • So I'm just going to fire up the process here, take a look

  • at the process ID.

  • And again, just do ADP log cat just on that process.

  • So as I page through the photos again, we see lots of

  • GC messages.

  • When I rotate, we're going to see the memory usage goes up

  • for a minute there.

  • But after a few collections, it does go back down to its

  • previous value.

  • So we've successfully eliminated the leak there.

  • And this is great.

  • You always want to eliminate memory leaks.

  • So that's an example of using the Eclipse Memory Analyzer to

  • debug a memory leak.

  • Eclipse Memory Analyzer is a really powerful tool.

  • It's a little bit complex.

  • It actually took me quite a while to figure out that these

  • were the two best tools for the job.

  • So you really want to watch out for these memory leaks.

  • So I gave an example here of retaining a long lived

  • reference to an activity.

  • If you've got our context, a view, a drawable, all of these

  • things you need to watch out for.

  • Don't hold long lived references to those.

  • It can also happen with non-static inner classes,

  • which is what I demonstrated there as well.

  • Runnable is actually one that can bite you sometimes.

  • You know, you create a new runnable.

  • You have a deferred event that's going to run in like

  • five minutes.

  • If user rotates the screen that deferred runnable is

  • going to hold your previous activity instance alive for

  • five minutes.

  • So that's not good.

  • You also want to watch out for caches.

  • Sometimes you have a cache and you want to keep memory alive,

  • so that you can load images faster let's say.

  • But you may inadvertently hold things alive too long.

  • So that covers basically, the core parts of the Eclipse

  • Memory Analyzer, and gives you a basic understanding of

  • memory leaks.

  • If you'd like to get more information about Memory

  • Analyzer, the download link you can find on the

  • eclipse.org/mat site.

  • Markus Kohler who's one of the original team members of

  • Eclipse Memory Analyzer, he has a blog called the Java

  • Performance Blog.

  • This is really great.

  • He's got tons of great articles on there about MAT

  • and different ways you can use it to understand your

  • applications memory usage.

  • I've also got an article that I wrote on the Android

  • Developer Blog called memory analysis for Android

  • applications.

  • It covers a lot of the same stuff that I

  • did in my demo here.

  • And Romain Guy also has a good article on avoiding memory

  • leaks in Android.

  • So I hope that's been helpful, I hope you guys have a better

  • understanding now of how you can figure out your apps

  • memory usage.

  • And I've talked about two of the biggest changes that we've

  • made in Gingerbread and Honeycomb that affect how your

  • apps use memory.

  • Thanks.

  • [APPLAUSE]

  • So I can take questions from the floor if anyone has any.

  • Or you all want to get out and get to a pub and have a beer?

  • AUDIENCE: Hi, you mentioned that if you use NIO in

  • Honeycomb your objects are going to be not in native

  • memory and now they're going to be managed memory.

  • How does that affect performance if you're doing an

  • IO, is that going to be any slower, like

  • very intense on network?

  • PATRICK DUBROY: No, I mean it shouldn't affect.

  • So I should say that there is still a way to allocate native

  • memory for your NIO byte buffers.

  • I'm not that familiar with the NIO APIs, but I believe

  • there's a way in JNI you can allocate your own memory.

  • So in that case, you'll still be using native memory.

  • But either way, it's just memory.

  • It's just allocated in a different place.

  • So there's nothing that makes the Dalvik heap memory slower

  • than other memory.

  • AUDIENCE: So you're saying how in Honeycomb the bitmaps are

  • stored in the Dalvik heap, but in previous versions to that

  • it was stored on native memory.

  • Does that mean that bitmaps had a different

  • amount of heap size?

  • Or is that still all counted in the 16 or 24 megabytes that

  • previous versions had?

  • PATRICK DUBROY: Yeah, good question.

  • The accounting limits are still the same.

  • That was accounted for previously.

  • You might have noticed if you ever ran into your heap limit,

  • you would be looking at your heap size and like, I haven't

  • hit the limit yet, why am I'm getting out of memory?

  • That was actually accounted for, so it was your total heap

  • size plus the amount of externally allocated memory

  • that was your limit.

  • So that hasn't changed.

  • AUDIENCE: Hello.

  • I have a question on when does the garbage

  • collector kicks in.

  • Is is when a number of objects in memory or

  • the size of the heap?

  • PATRICK DUBROY: Well, it depends on what kind of

  • garbage collection you're talking about.

  • The concurrent garbage collector--

  • AUDIENCE: Yeah, the concurrent.

  • Yes.

  • PATRICK DUBROY: Yeah, so that I believe is the amount of

  • basically, how full your heap is getting.

  • AUDIENCE: Because I noticed that when you do a lot of

  • [INAUDIBLE]

  • provide operations, so you have like [INAUDIBLE]

  • list of operations, the garbage collector kicks in.

  • But actually don't collect any objects because you're just

  • filling in the array of objects that you want to

  • insert into a database.

  • And that's grow quite quickly.

  • And that tends to slow down a bit, the application without

  • actually solving any heap size.

  • PATRICK DUBROY: Yeah, I'm not sure if the GC looks at--

  • so you're basically saying, I guess, that the collector is

  • kicking in.

  • It's not actually able to collect anything, so it

  • shouldn't--

  • AUDIENCE: But it keeps trying.

  • PATRICK DUBROY: Yeah, it should be smart enough.

  • Yeah, I don't believe we actually look at those kind of

  • statistics yet.

  • But I mean it seems reasonable.

  • Yeah.

  • AUDIENCE: I was wondering if you guys have some plans for

  • making a profiler for applications or more tools for

  • analyzing memory and all that stuff?

  • PATRICK DUBROY: No plans that I know of.

  • Is there anything in particular that you need?

  • I mean I think the Eclipse Memory Analyzer is a really

  • powerful tool and I use it in my day-to-day

  • work quite a bit.

  • So I've certainly never found that it it was missing certain

  • features that I needed.

  • AUDIENCE: Yeah, probably because there are some old

  • versions from Android that show

  • memory leaks or something.

  • But for example, on Eclair, there were

  • some stuff with the--

  • something there.

  • PATRICK DUBROY: Yeah, I mean we don't have any immediate

  • plans I don't think to running specific tools.

  • AUDIENCE: OK, thank you.

  • PATRICK DUBROY: Oh, sorry I've been--

  • yeah.

  • AUDIENCE: To my understanding, the native part of a bitmap

  • memory before was actually an instance of the SKIA library,

  • of one of the SKIA library bitmap classes.

  • So is this still there or is it gone now that there is no

  • more native memory allocated?

  • PATRICK DUBROY: No, SKIA is still part

  • of this stack there.

  • Basically at the point where SKIA calls out to allocate

  • memory, we actually just call back into the VM and allocate

  • the memory there rather than calling malloc.

  • So it's still basically the same mechanism, but the

  • memory's just coming from a different place.

  • AUDIENCE: OK.

  • AUDIENCE: I thought that when I was using my application, I

  • checked the heap size.

  • While using the application the heap size was not

  • significantly going up.

  • But the amount of memory used by the application, which is

  • listed in the applications tab under the running applications

  • is going up significantly.

  • Sometimes even doubling.

  • I know that this is a different heap

  • that is shown there.

  • It's actually the process heap, right?

  • Can you tell me what the background of that is that

  • this is shown there because might like--

  • I don't have a memory leak and users complain about my

  • application leaking memory.

  • Because for the user it looks like it's leaking memory.

  • PATRICK DUBROY: Right.

  • Because you're saying there's stuff that's attributed to

  • your process that are showing up in the--

  • basically, in system memory?

  • AUDIENCE: Yeah.

  • So it's showing the system memory in the applications

  • tab, which is not really linked to my heap memory.

  • So that is going up, but I can only control the heap memory.

  • If I don't have a native application I cannot control

  • everything else.

  • PATRICK DUBROY: I mean there are going to be various things

  • in the system that are going to get larger.

  • For example, like your JIT code caches.

  • As the JIT kicks in and is allocating memory, like it

  • needs to store the compiled code somewhere.

  • So there's definitely other parts of this system that

  • allocate memory that's going to kind of get charged to your

  • application.

  • But I can't think of why.

  • I can't think of anything that would be out of the ordinary

  • really that should cause problems.

  • AUDIENCE: But do you know if this will be changed maybe in

  • the future?

  • That this number is not shown there because for me, it

  • doesn't make sense to show this number to the end user

  • because he doesn't understand what it means.

  • PATRICK DUBROY: I see.

  • Where is he seeing the number?

  • AUDIENCE: In the running applications tab.

  • If he goes to settings, running applications, he can

  • see the memory usage per application and that's

  • actually the system memory.

  • PATRICK DUBROY: I see.

  • Yeah, I'm not sure what our plans are with that.

  • Sorry.

  • I can take a look and I'm not actually sure where it's

  • getting that number from.

  • AUDIENCE: OK, thanks.

  • AUDIENCE: My question's about reasonable expectations of out

  • of memory errors.

  • Is it possible to completely eliminate them?

  • We've been working for a while in getting rid of all the out

  • of memory errors and down to one in about

  • every 17,000 sessions.

  • Should we keep troubleshooting.

  • I mean, I'd like to get it down to zero, but is that

  • reasonable or?

  • PATRICK DUBROY: So there are certain scenarios where if

  • you're really close to your memory limit, so if your

  • applications live memory size is really close to that limit,

  • the garbage collector's fundamentally kind of

  • asynchronous.

  • So if you're really close to the limit, there can be times

  • where you're just trying to allocate so fast that the

  • garbage collector can't keep up.

  • So you can be actually sort of out

  • running the garbage collector.

  • So certainly it's possible to build applications that never

  • see an out of memory error.

  • But on the other hand, there are certain types of

  • applications that are going to be running really, really

  • close to the limits.

  • One thing you can use if you have caches or things that you

  • can free up, there are several ways to figure out that you're

  • getting close to the heap memory limit.

  • I believe there's a callback you can get notification that

  • we're getting low on memory.

  • Although, the name escapes me.

  • But you can also look at that, the Activity Manager, get

  • memory class to get a sense of how much memory you have

  • available on the system.

  • And you know, maybe you can keep like smaller caches or

  • leave the initialize objects rather than initializing them

  • all in the constructor or something like that.

  • It really depends on the application whether you expect

  • to be running close to that heap limit or not.

  • AUDIENCE: You recommended not to call system.gc manually if

  • you can help it.

  • Is there any way to reliably free bitmap memory

  • pre-Honeycomb?

  • PATRICK DUBROY: Yes.

  • Pre-Honeycomb?

  • AUDIENCE: Yes.

  • PATRICK DUBROY: You can call recycle on the bitmap.

  • AUDIENCE: Yeah, but it can still take several passes

  • apparently.

  • PATRICK DUBROY: No.

  • If you call recycle that will immediately

  • free the backing memory.

  • The bitmap itself, that's like 80 bytes or something.

  • AUDIENCE: There are also bitmaps like drawables that

  • you can't manually recycle the bitmaps that the drawable

  • object creates.

  • PATRICK DUBROY: OK.

  • AUDIENCE: The backing bitmaps for those.

  • PATRICK DUBROY: I see.

  • No, I mean there are still some cases I guess where

  • system.gc is the right approach.

  • [UNINTELLIGIBLE PHRASE]

  • PATRICK DUBROY: OK, which objects are you

  • talking about in--

  • AUDIENCE: My experience is when I have image drawables

  • that are used some where in my layout and I know they're no

  • longer needed.

  • Some of them are fairly large and it seems like--

  • PATRICK DUBROY: You can call recycle on those I believe.

  • AUDIENCE: OK.

  • My experience is that it will cause other

  • problems when I do that.

  • PATRICK DUBROY: If you're still using

  • them, then you can't--

  • I mean, you can only recycle that when you're not using it.

  • AUDIENCE: Sure.

  • OK.

  • AUDIENCE: For native code that uses a lot of mallocs, what's

  • the best way to manage that memory?

  • PATRICK DUBROY: That's a very good question.

  • When you've got native code, I mean mostly what I was

  • covering here was managing memory from the

  • Dalvik side of things.

  • I don't know that I have any real pointers.

  • I mean that's one of the reasons why programming in a

  • managed run time is very nice.

  • Is that you don't have to deal with manually

  • managing your memory.

  • I don't have any great advice for that.

  • AUDIENCE: Does the app that calls into the native

  • libraries, is it aware of, at least on an aggravate level,

  • how much memory is being used or is it

  • completely a separate--

  • PATRICK DUBROY: I don't believe there's any way to

  • account for if you're calling into the library and it's

  • calling malloc.

  • I don't know that there's any way to account for that memory

  • from your application side.

  • AUDIENCE: But that garbage collector will run when you

  • start allocating memory, will it not?

  • PATRICK DUBROY: It'll run when you start allocating like

  • objects in Dalvik.

  • It doesn't have any knowledge of calls to malloc.

  • AUDIENCE: You'll just get an out of memory or a failed

  • malloc if you--

  • PATRICK DUBROY: Yeah.

  • Sure.

  • It's going to be the same mechanisms as

  • any C or C++ program.

  • Malloc is going to return a null pointer.

  • Yes?

  • AUDIENCE: [UNINTELLIGIBLE PHRASE]

  • PATRICK DUBROY: Pardon me?

  • AUDIENCE: [UNINTELLIGIBLE PHRASE]

  • PATRICK DUBROY: Oh, OK.

  • That's news to me.

  • Malloc can't fail on Android.

  • AUDIENCE: [UNINTELLIGIBLE PHRASE]

  • PATRICK DUBROY: I see.

  • OK.

  • AUDIENCE: Can you repeat that?

  • PATRICK DUBROY: Romain tells me that malloc

  • can't fail on Android.

  • AUDIENCE: [UNINTELLIGIBLE PHRASE]

  • PATRICK DUBROY: I see.

  • So I think this is the old Linux lazy--

  • yeah.

  • It'll successfully allocate the virtual memory, but Linux

  • can actually hand out more virtual memory than it can

  • actually commit.

  • So you can get problems. Like when your system is totally,

  • totally out of native memory, you're going to see crashes.

  • AUDIENCE: So native memory is completely separate from

  • anything Dalvik?

  • PATRICK DUBROY: Yes.

  • Well, I mean, sorry, I should say, like Dalvik is still

  • allocating its own memory like for the heap through the

  • native mechanisms. So it's reserving the same virtual

  • memory pages that other applications are using.

  • AUDIENCE: But if your system memory is--

  • PATRICK DUBROY: Yeah, if your system memory is out, you're

  • in trouble.

  • AUDIENCE: But Dalvik won't get a notice say, hey, better

  • start garbage collecting?

  • PATRICK DUBROY: Well, no.

  • AUDIENCE: The flag for using larger heap, does that require

  • a permission, like users permission or

  • something like that?

  • PATRICK DUBROY: I can't remember whether we

  • added that or not.

  • I don't think that it does.

  • AUDIENCE: Like the whole--

  • could it been like a permission thing?

  • But if it's not then--

  • PATRICK DUBROY: Yeah, I mean the idea I think is that--

  • yeah, you're right.

  • I mean it can affect the system as a whole because

  • you're going to have apps that are using a lot more memory,

  • which is why I gave that big warning, that this is not

  • something that you should be using unless you know that you

  • really need it.

  • AUDIENCE: Yeah.

  • But [INAUDIBLE].

  • OK.

  • PATRICK DUBROY: I don't think there's a

  • permission for it, though.

  • AUDIENCE: What if the app kind of runs in the background for

  • weeks at a time?

  • So I do everything I can to simulate a leak, click

  • everywhere I can, but I see the leaks if the app runs two

  • or three days and then I get [INAUDIBLE].

  • PATRICK DUBROY: One thing you could try is if you can use

  • the APIs to determine how much free memory you have. I don't

  • know if there's any way you can actually kind of notice in

  • your application that it started leaking.

  • But you could write out an HPROF file when you notice

  • that you've gotten to a certain point, your heap is

  • getting smaller and smaller.

  • So there is some debug information there

  • that you could use.

  • So if you have like some beta testers, who could actually

  • send you these dumps, then you could do that.

  • So write out the HPROF file to SD card or something.

  • AUDIENCE: So maybe I can just write an HPROF file every--

  • PATRICK DUBROY: I wouldn't do that.

  • I mean they're quite large.

  • You don't want to be doing that on a regular basis.

  • But if you detect that things have gone really, really wrong

  • and you're about to die, in an alpha version or something for

  • testing that's one way you could do it.

  • But I definitely wouldn't recommend putting an app in

  • the market that's dumping like very large files to the SD

  • card for no reason.

  • AUDIENCE: OK.

  • PATRICK DUBROY: OK, thanks a lot.

PATRICK DUBROY: Hi everybody, my name's Patrick Dubroy and

字幕與單字

單字即點即查 點擊單字可以查詢單字解釋

B1 中級

谷歌I/O 2011。Android應用程序的內存管理 (Google I/O 2011: Memory management for Android Apps)

  • 128 15
    Hhart Budha 發佈於 2021 年 01 月 14 日
影片單字