Practice English Speaking&Listening with: Google I/O 2011: Memory management for Android Apps

Normal
(0)
Difficulty: 0

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.

The Description of Google I/O 2011: Memory management for Android Apps