Let's use DDMS to examine the heap usage of this app. You can start up DDMS in one of two ways:
- from Eclipse: click Window > Open Perspective > Other... > DDMS
- or from the command line: run
ddms
(or./ddms
on Mac/Linux) in thetools/
directory
Select the process
com.example.android.hcgallery
in the left pane, and then click the Show heap updates button in the toolbar. Then, switch to the VM Heap tab in DDMS. It shows some basic stats about our heap memory usage, updated after every GC. To see the first update, click the Cause GC button.
We can see that our live set (the Allocated column) is a little over 8MB. Now flip through the photos, and watch that number go up. Since there are only 13 photos in this app, the amount of memory we leak is bounded. In some ways, this is the worst kind of leak to have, because we never get an
OutOfMemoryError
indicating that we are leaking.Creating a heap dump
Let's use a heap dump to track down the problem. Click the Dump HPROF file button in the DDMS toolbar, choose where you want to save the file, and then run
hprof-conv
on it. In this example, I'll be using the standalone version of MAT (version 1.0.1), available from the MAT download site.
If you're running ADT (which includes a plug-in version of DDMS) and have MAT installed in Eclipse as well, clicking the “dump HPROF” button will automatically do the conversion (using hprof-conv) and open the converted hprof file into Eclipse (which will be opened by MAT).
Analyzing heap dumps using MAT
Start up MAT and load the converted HPROF file we just created. MAT is a powerful tool, and it's beyond the scope of this article to explain all it's features, so I'm just going to show you one way you can use it to detect a leak: the Histogram view. The Histogram view shows a list of classes sortable by the number of instances, the shallow heap (total amount of memory used by all instances), or the retained heap (total amount of memory kept alive by all instances, including other objects that they have references to).
If we sort by shallow heap, we can see that instances of
byte[]
are at the top. As of Android 3.0 (Honeycomb), the pixel data for Bitmap objects is stored in byte arrays (previously it was not stored in the Dalvik heap), and based on the size of these objects, it's a safe bet that they are the backing memory for our leaked bitmaps.
Right-click on the
byte[]
class and select List Objects > with incoming references. This produces a list of all byte arrays in the heap, which we can sort based on Shallow Heap usage.
Pick one of the big objects, and drill down on it. This will show you the path from the root set to the object -- the chain of references that keeps this object alive. Lo and behold, there's our bitmap cache!
MAT can't tell us for sure that this is a leak, because it doesn't know whether these objects are needed or not -- only the programmer can do that. In this case, the cache is using a large amount of memory relative to the rest of the application, so we might consider limiting the size of the cache.
Comparing heap dumps with MAT
When debugging memory leaks, sometimes it's useful to compare the heap state at two different points in time. To do this, you'll need to create two separate HPROF files (don't forget to convert them using
hprof-conv
).
Here's how you can compare two heap dumps in MAT (it's a little complicated):
- Open the first HPROF file (using File > Open Heap Dump).
- Open the Histogram view.
- In the Navigation History view (use Window > Navigation History if it's not visible), right click on histogram and selectAdd to Compare Basket.
- Open the second HPROF file and repeat steps 2 and 3.
- Switch to the Compare Basket view, and click Compare the Results (the red "!" icon in the top right corner of the view).
No comments:
Post a Comment