From a3f0e01f332ee0acc8450bf4564992f2ffe2f426 Mon Sep 17 00:00:00 2001 From: Scott Main Date: Thu, 19 Sep 2013 17:45:40 -0700 Subject: add docs about managing app memory Change-Id: I8bd7b05e7ce00faedbbc131a38f2a73637d1b365 --- docs/html/images/tools/mat-histogram@2x.png | Bin 0 -> 603209 bytes docs/html/images/tools/monitor-hprof@2x.png | Bin 0 -> 446525 bytes docs/html/images/tools/monitor-tracker@2x.png | Bin 0 -> 887658 bytes docs/html/images/tools/monitor-vmheap@2x.png | Bin 0 -> 588535 bytes docs/html/tools/debugging/debugging-memory.jd | 494 +++++++++++++++++ docs/html/tools/help/monitor.jd | 17 + docs/html/tools/tools_toc.cs | 1 + docs/html/training/articles/memory.jd | 740 ++++++++++++++++++++++++++ docs/html/training/articles/perf-anr.jd | 2 +- docs/html/training/articles/perf-jni.jd | 2 +- docs/html/training/articles/perf-tips.jd | 2 +- docs/html/training/training_toc.cs | 8 + 12 files changed, 1263 insertions(+), 3 deletions(-) create mode 100644 docs/html/images/tools/mat-histogram@2x.png create mode 100644 docs/html/images/tools/monitor-hprof@2x.png create mode 100644 docs/html/images/tools/monitor-tracker@2x.png create mode 100644 docs/html/images/tools/monitor-vmheap@2x.png create mode 100644 docs/html/tools/debugging/debugging-memory.jd create mode 100644 docs/html/training/articles/memory.jd (limited to 'docs') diff --git a/docs/html/images/tools/mat-histogram@2x.png b/docs/html/images/tools/mat-histogram@2x.png new file mode 100644 index 0000000..feb9654 Binary files /dev/null and b/docs/html/images/tools/mat-histogram@2x.png differ diff --git a/docs/html/images/tools/monitor-hprof@2x.png b/docs/html/images/tools/monitor-hprof@2x.png new file mode 100644 index 0000000..7afb9c7 Binary files /dev/null and b/docs/html/images/tools/monitor-hprof@2x.png differ diff --git a/docs/html/images/tools/monitor-tracker@2x.png b/docs/html/images/tools/monitor-tracker@2x.png new file mode 100644 index 0000000..a0bc329 Binary files /dev/null and b/docs/html/images/tools/monitor-tracker@2x.png differ diff --git a/docs/html/images/tools/monitor-vmheap@2x.png b/docs/html/images/tools/monitor-vmheap@2x.png new file mode 100644 index 0000000..ef4b9c2 Binary files /dev/null and b/docs/html/images/tools/monitor-vmheap@2x.png differ diff --git a/docs/html/tools/debugging/debugging-memory.jd b/docs/html/tools/debugging/debugging-memory.jd new file mode 100644 index 0000000..0454293 --- /dev/null +++ b/docs/html/tools/debugging/debugging-memory.jd @@ -0,0 +1,494 @@ +page.title=Investigating Your RAM Usage +page.tags="memory","OutOfMemoryError" +@jd:body + +
+ +
+ + + + +

Because Android is designed for mobile devices, you should always be careful about how much +random-access memory (RAM) your app uses. Although Android’s Dalvik virtual machine performs +routine garbage collection, this doesn’t mean you can ignore when and where your app allocates and +releases memory. In order to provide a stable user experience that allows the system to quickly +switch between apps, it’s important that your app does not needlessly consume memory when the user +is not interacting with it.

+ +

Even if you follow all the best practices for Managing Your App Memory during +development (which you should), you still might leak objects or introduce other memory bugs. The +only way to be certain your app is using as little memory as possible is to analyze your app’s +memory usage with tools. This guide shows you how to do that.

+ + +

Interpreting Log Messages

+ +

The simplest place to begin investigating your apps memory usage is the Dalvik log messages. You'll +find these log messages in logcat (the output is +available in the Device Monitor or directly in IDEs such as Eclipse and Android Studio).

+ +

Every time a garbage collection occurs, logcat prints a message with the following information:

+ +
+D/dalvikvm: <GC_Reason> <Amount_freed>, <Heap_stats>, <External_memory_stats>, <Pause_time>
+
+ +
+
GC Reason
+
+What triggered the garbage collection and what kind of collection it is. Reasons that may appear +include: +
+
GC_CONCURRENT
+
A concurrent garbage collection that frees up memory as your heap begins to fill up.
+ +
GC_FOR_MALLOC
+
A garbage collection caused because your app attempted to allocate memory when your heap was +already full, so the system had to stop your app and reclaim memory.
+ +
GC_HPROF_DUMP_HEAP
+
A garbage collection that occurs when you create an HPROF file to analyze your heap.
+ +
GC_EXPLICIT +
An explicit garbage collection, such as when you call {@link java.lang.System#gc()} (which you +should avoid calling and instead trust the garbage collector to run when needed).
+ +
GC_EXTERNAL_ALLOC
+
This happens only on API level 10 and lower (newer versions allocate everything in the Dalvik +heap). A garbage collection for externally allocated memory (such as the pixel data stored in +native memory or NIO byte buffers).
+
+
+ +
Amount freed
+
The amount of memory reclaimed from this garbage collection.
+ +
Heap stats
+
Percentage free and (number of live objects)/(total heap size).
+ +
External memory stats
+
Externally allocated memory on API level 10 and lower (amount of allocated memory) / (limit at +which collection will occur).
+ +
Pause time
+
Larger heaps will have larger pause times. Concurrent pause times show two pauses: one at the +beginning of the collection and another near the end.
+
+ +

For example:

+ +
+D/dalvikvm( 9050): GC_CONCURRENT freed 2049K, 65% free 3571K/9991K, external 4703K/5261K, paused 2ms+2ms
+
+ +

As these log messages stack up, look out for increases in the heap stats (the +{@code 3571K/9991K} value in the above example). If this value +continues to increase and doesn't ever seem to get smaller, you could have a memory leak.

+ + +

Viewing Heap Updates

+ +

To get a little information about what kind of memory your app is using and when, you can view +real-time updates to your app's heap in the Device Monitor:

+ +
    +
  1. Open the Device Monitor. +

    From your <sdk>/tools/ directory, launch the monitor tool.

    +
  2. +
  3. In the Debug Monitor window, select your app's process from the list on the left.
  4. +
  5. Click Update Heap above the process list.
  6. +
  7. In the right-side panel, select the Heap tab.
  8. +
+ +

The Heap view shows some basic stats about your heap memory usage, updated after every +garbage collection. To see the first update, click the Cause GC button.

+ + +

Figure 1. The Device Monitor tool, +showing the [1] Update Heap and [2] Cause GC buttons. +The Heap tab on the right shows the heap results.

+ +

Continue interacting with your app to watch your heap allocation update with each garbage +collection. This can help you identify which actions in your app are likely causing too much +allocation and where you should try to reduce allocations and release +resources.

+ + + +

Tracking Allocations

+ +

As you start narrowing down memory issues, you should also use the Allocation Tracker to +get a better understanding of where your memory-hogging objects are allocated. The Allocation +Tracker can be useful not only for looking at specific uses of memory, but also to analyze critical +code paths in an app such as scrolling.

+ +

For example, tracking allocations when flinging a list in your app allows you to see all the +allocations that need to be done for that behavior, what thread they are on, and where they came +from. This is extremely valuable for tightening up these paths to reduce the work they need and +improve the overall smoothness of the UI.

+ +

To use Allocation Tracker:

+
    +
  1. Open the Device Monitor. +

    From your <sdk>/tools/ directory, launch the monitor tool.

    +
  2. +
  3. In the DDMS window, select your app's process in the left-side panel.
  4. +
  5. In the right-side panel, select the Allocation Tracker tab.
  6. +
  7. Click Start Tracking.
  8. +
  9. Interact with your app to execute the code paths you want to analyze.
  10. +
  11. Click Get Allocations every time you want to update the +list of allocations.
  12. +
+ +

The list shows all recent allocations, +currently limited by a 512-entry ring buffer. Click on a line to see the stack trace that led to +the allocation. The trace shows you not only what type of object was allocated, but also in which +thread, in which class, in which file and at which line.

+ + +

Figure 2. The Device Monitor tool, +showing recent app allocations and stack traces in the Allocation Tracker.

+ + +

Note: You will always see some allocations from {@code +DdmVmInternal} and else where that come from the allocation tracker itself.

+ +

Although it's not necessary (nor possible) to remove all allocations for your performance +critical code paths, the allocation tracker can help you identify important issues in your code. +For instance, some apps might create a new {@link android.graphics.Paint} object on every draw. +Moving that object into a global member is a simple fix that helps improve performance.

+ + + + + + +

Viewing Overall Memory Allocations

+ +

For further analysis, you may want to observe how that your app's memory is +divided between different categories, which you can do with the adb meminfo data.

+ +

When talking about how much RAM your app is using with this data, the key metrics +discussed below are:

+ +
+
Private (Clean and Dirty) RAM
+
This is memory that is being used by only your process. This is the bulk of the RAM that the system +can reclaim when your app’s process is destroyed. Generally, the most important portion of this is +“private dirty” RAM, which is the most expensive because it is used by only your process and its +contents exist only in RAM so can’t be paged to storage (because Android does not use swap). All +Dalvik and native heap allocations you make will be private dirty RAM; Dalvik and native +allocations you share with the Zygote process are shared dirty RAM.
+ +
Proportional Set Size (PSS)
+
This is a measurement of your app’s RAM use that takes into account sharing pages across processes. +Any RAM pages that are unique to your process directly contribute to its PSS value, while pages +that are shared with other processes contribute to the PSS value only in proportion to the amount +of sharing. For example, a page that is shared between two processes will contribute half of its +size to the PSS of each process.
+
+ + +

A nice characteristic of the PSS measurement is that you can add up the PSS across all processes to +determine the actual memory being used by all processes. This means PSS is a good measure for the +actual RAM weight of a process and for comparison against the RAM use of other processes and the +total available RAM.

+ +

You can look at the memory use of your app (measured in kilobytes) with the +following adb command:

+ +
+adb shell dumpsys meminfo <package_name>
+
+ +

For example, below is the the output for Gmail’s process on a tablet device. There is a lot of +information here, but key points for discussion are highlighted in different colors.

+ +

Note: The information you see may vary slightly from what is shown +here, as some details of the output differ across platform versions.

+ +
+** MEMINFO in pid 9953 [com.google.android.gm] **
+                 Pss     Pss  Shared Private  Shared Private    Heap    Heap    Heap
+               Total   Clean   Dirty   Dirty   Clean   Clean    Size   Alloc    Free
+              ------  ------  ------  ------  ------  ------  ------  ------  ------
+  Native Heap      0       0       0       0       0       0    7800    7637(6)  126
+  Dalvik Heap   5110(3)    0    4136    4988(3)    0       0    9168    8958(6)  210
+ Dalvik Other   2850       0    2684    2772       0       0
+        Stack     36       0       8      36       0       0
+       Cursor    136       0       0     136       0       0
+       Ashmem     12       0      28       0       0       0
+    Other dev    380       0      24     376       0       4
+     .so mmap   5443(5) 1996    2584    2664(5) 5788    1996(5)
+    .apk mmap    235      32       0       0    1252      32
+    .ttf mmap     36      12       0       0      88      12
+    .dex mmap   3019(5) 2148       0       0    8936    2148(5)
+   Other mmap    107       0       8       8     324      68
+      Unknown   6994(4)    0     252    6992(4)    0       0
+        TOTAL  24358(1) 4188    9724   17972(2)16388    4260(2)16968   16595     336
+ 
+ Objects
+               Views:    426         ViewRootImpl:        3(8)
+         AppContexts:      6(7)        Activities:        2(7)
+              Assets:      2        AssetManagers:        2
+       Local Binders:     64        Proxy Binders:       34
+    Death Recipients:      0
+     OpenSSL Sockets:      1
+ 
+ SQL
+         MEMORY_USED:   1739
+  PAGECACHE_OVERFLOW:   1164          MALLOC_SIZE:       62
+
+ +

Generally, you should be concerned with only the Pss Total and Private Dirty +columns. In some cases, the Private Clean and Heap Alloc columns also offer +interesting data. Here is some more information about the different memory allocations (the rows) +you should observe: + +

+
Dalvik Heap
+
The RAM used by Dalvik allocations in your app. The Pss Total includes all Zygote +allocations (weighted by their sharing across processes, as described in the PSS definition above). +The Private Dirty number is the actual RAM committed to only your app’s heap, composed of +your own allocations and any Zygote allocation pages that have been modified since forking your +app’s process from Zygote. + +

Note: On newer platform versions that have the Dalvik +Other section, the Pss Total and Private Dirty numbers for Dalvik Heap do +not include Dalvik overhead such as the just-in-time compilation (JIT) and garbage collection (GC) +bookkeeping, whereas older versions list it all combined under Dalvik.

+ +

The Heap Alloc is the amount of memory that the Dalvik and native heap allocators keep +track of for your app. This value is larger than Pss Total and Private Dirty +because your process was forked from Zygote and it includes allocations that your process shares +with all the others.

+
+ +
.so mmap and .dex mmap
+
The RAM being used for mmapped .so (native) and .dex (Dalvik) code. The +Pss Total number includes platform code shared across apps; the Private Clean is +your app’s own code. Generally, the actual mapped size will be much larger—the RAM here is only +what currently needs to be in RAM for code that has been executed by the app. However, the .so mmap +has a large private dirty, which is due to fix-ups to the native code when it was loaded into its +final address. +
+ +
Unknown
+
Any RAM pages that the system could not classify into one of the other more specific items. +Currently, this contains mostly native allocations, which cannot be identified by the tool when +collecting this data due to Address Space Layout Randomization (ASLR). As with the Dalvik heap, the +Pss Total for Unknown takes into account sharing with Zygote, and Private Dirty +is unknown RAM dedicated to only your app. +
+ +
TOTAL
+
The total Proportional Set Size (PSS) RAM used by your process. This is the sum of all PSS fields +above it. It indicates the overall memory weight of your process, which can be directly compared +with other processes and the total available RAM. + +

The Private Dirty and Private Clean are the total allocations within your +process, which are not shared with other processes. Together (especially Private Dirty), +this is the amount of RAM that will be released back to the system when your process is destroyed. +Dirty RAM is pages that have been modified and so must stay committed to RAM (because there is no +swap); clean RAM is pages that have been mapped from a persistent file (such as code being +executed) and so can be paged out if not used for a while.

+ +
+ +
ViewRootImpl
+
The number of root views that are active in your process. Each root view is associated with a +window, so this can help you identify memory leaks involving dialogs or other windows. +
+ +
AppContexts and Activities
+
The number of app {@link android.content.Context} and {@link android.app.Activity} objects that +currently live in your process. This can be useful to quickly identify leaked {@link +android.app.Activity} objects that can’t be garbage collected due to static references on them, +which is common. These objects often have a lot of other allocations associated with them and so +are a good way to track large memory leaks.
+ +

Note: A {@link android.view.View} or {@link +android.graphics.drawable.Drawable} object also holds a reference to the {@link +android.app.Activity} that it's from, so holding a {@link android.view.View} or {@link +android.graphics.drawable.Drawable} object can also lead to your app leaking an {@link +android.app.Activity}.

+ + +
+ + + + + + + + + +

Capturing a Heap Dump

+ +

A heap dump is a snapshot of all the objects in your app's heap, stored in a binary format called +HPROF. Your app's heap dump provides information about the overall state of your app's heap so you +can track down problems you might have identified while viewing heap updates.

+ +

To retrieve your heap dump:

+
    +
  1. Open the Device Monitor. +

    From your <sdk>/tools/ directory, launch the monitor tool.

    +
  2. +
  3. In the DDMS window, select your app's process in the left-side panel.
  4. +
  5. Click Dump HPROF file, shown in figure 3.
  6. +
  7. In the window that appears, name your HPROF file, select the save location, +then click Save.
  8. +
+ + +

Figure 3. The Device Monitor tool, +showing the [1] Dump HPROF file button.

+ +

If you need to be more precise about when the dump is created, you can also create a heap dump +at the critical point in your app code by calling {@link android.os.Debug#dumpHprofData +dumpHprofData()}.

+ +

The heap dump is provided in a format that's similar to, but not identical to one from the Java +HPROF tool. The major difference in an Android heap dump is due to the fact that there are a large +number of allocations in the Zygote process. But because the Zygote allocations are shared across +all app processes, they don’t matter very much to your own heap analysis.

+ +

To analyze your heap dump, you can use a standard tool like jhat or the Eclipse Memory Analyzer Tool (MAT). However, first +you'll need to convert the HPROF file from Android's format to the J2SE HPROF format. You can do +this using the hprof-conv tool provided in the <sdk>/tools/ +directory. Simply run the hprof-conv command with two arguments: the original HPROF +file and the location to write the converted HPROF file. For example:

+ +
+hprof-conv heap-original.hprof heap-converted.hprof
+
+ +

Note: If you're using the version of DDMS that's integrated into +Eclipse, you do not need to perform the HPROF converstion—it performs the conversion by +default.

+ +

You can now load the converted file in MAT or another heap analysis tool that understands +the J2SE HPROF format.

+ +

When analyzing your heap, you should look for memory leaks caused by:

+ + + +

Using the Eclipse Memory Analyzer Tool

+ +

The Eclipse Memory Analyzer Tool (MAT) is just one +tool that you can use to analyze your heap dump. It's also quite powerful so most of its +capabilities are beyond the scope of this document, but here are a few tips to get you started. + +

Once you open your converted HPROF file in MAT, you'll see a pie chart in the Overview, +showing what your largest objects are. Below this chart, are links to couple of useful features:

+ + + + + +

Figure 4. The Eclipse Memory Analyzer Tool (MAT), +showing the Histogram view and a search for "MainActivity".

+ +

For more information about MAT, watch the Google I/O 2011 presentation, +Memory management for Android apps, +which includes a walkthrough using MAT beginning at about 21:10. +Also refer to the Eclipse Memory +Analyzer documentation.

+ +

Comparing heap dumps

+ +

You may find it useful to compare your app's heap state at two different points in time in order +to inspect the changes in memory allocation. To compare two heap dumps using MAT:

+ +
    +
  1. Create two HPROF files as described above, in Capturing a Heap Dump. +
  2. Open the first HPROF file in MAT (File > Open Heap Dump). +
  3. In the Navigation History view (if not visible, select Window > + Navigation History), right-click on Histogram and select + Add to Compare Basket. +
  4. Open the second HPROF file and repeat steps 2 and 3. +
  5. Switch to the Compare Basket view and click Compare the Results + (the red "!" icon in the top-right corner of the view). +
+ + + + + + +

Triggering Memory Leaks

+ +

While using the tools described above, you should aggressively stress your app code and try +forcing memory leaks. One way to provoke memory leaks in your app is to let it +run for a while before inspecting the heap. Leaks will trickle up to the top of the allocations in +the heap. However, the smaller the leak, the longer you need to run the app in order to see it.

+ +

You can also trigger a memory leak in one of the following ways:

+
    +
  1. Rotate the device from portrait to landscape and back again multiple times while in different +activity states. Rotating the device can often cause an app to leak an {@link android.app.Activity}, +{@link android.content.Context}, or {@link android.view.View} object because the system +recreates the {@link android.app.Activity} and if your app holds a reference +to one of those objects somewhere else, the system can't garbage collect it.
  2. +
  3. Switch between your app and another app while in different activity states (navigate to +the Home screen, then return to your app).
  4. +
+ +

Tip: You can also perform the above steps by using the "monkey" +test framework. For more information on running the monkey test framework, read the monkeyrunner +documentation.

\ No newline at end of file diff --git a/docs/html/tools/help/monitor.jd b/docs/html/tools/help/monitor.jd index 18fb49a..e1fe772 100644 --- a/docs/html/tools/help/monitor.jd +++ b/docs/html/tools/help/monitor.jd @@ -1,6 +1,18 @@ page.title=Device Monitor @jd:body +
+
+

See also

+ +
    +
  1. Investigating Your RAM Usage
  2. +
+
+
+ +

Android Device Monitor is a stand-alone tool that provides a graphical user interface for several Android application debugging and analysis tools. The Monitor tool does not require installation of a integrated development environment, such as Eclipse, and encapsulates the @@ -14,6 +26,7 @@ following tools:

  • Pixel Perfect magnification viewer
  • +

    Usage

    To start Device Monitor, enter the following command from the SDK tools/ @@ -22,3 +35,7 @@ directory:

    Start an Android emulator or connect an Android device via USB cable, and connect Device Monitor to the device by selecting it in the Devices window.

    + +

    Note: Only one debugger can be connected to your device at a time. +If you're using ADT, you may need to close the debugging tool before launching the Device Monitor +in order for the device to be fully debuggable.

    diff --git a/docs/html/tools/tools_toc.cs b/docs/html/tools/tools_toc.cs index 8ad61ec..39eecf8 100644 --- a/docs/html/tools/tools_toc.cs +++ b/docs/html/tools/tools_toc.cs @@ -139,6 +139,7 @@
  • Optimizing your UI
  • Profiling with Traceview and dmtracedump
  • Analysing Display and Performance with Systrace
  • +
  • Investigating Your RAM Usage
  • Using the Dev Tools App
  • diff --git a/docs/html/training/articles/memory.jd b/docs/html/training/articles/memory.jd new file mode 100644 index 0000000..cdc0cd4 --- /dev/null +++ b/docs/html/training/articles/memory.jd @@ -0,0 +1,740 @@ +page.title=Managing Your App's Memory +page.tags="ram","low memory","OutOfMemoryError","onTrimMemory" +page.article=true +@jd:body + + +
    + +
    + + +

    Random-access memory (RAM) is a valuable resource in any software development environment, but +it's even more valuable on a mobile operating system where physical memory is often constrained. +Although Android's Dalvik virtual machine performs routine garbage collection, this doesn't allow +you to ignore when and where your app allocates and releases memory.

    + +

    In order for the garbage collector to reclaim memory from your app, you need to avoid +introducing memory leaks (usually caused by holding onto object references in global members) and +release any {@link java.lang.ref.Reference} objects at the appropriate time (as defined by +lifecycle callbacks discussed further below). For most apps, the Dalvik garbage collector takes +care of the rest: the system reclaims your memory allocations when the corresponding objects leave +the scope of your app's active threads.

    + +

    This document explains how Android manages app processes and memory allocation, and how you can +proactively reduce memory usage while developing for Android. For more information about general +practices to clean up your resources when programming in Java, refer to other books or online +documentation about managing resource references. If you’re looking for information about how to +analyze your app’s memory once you’ve already built it, read Investigating Your RAM Usage.

    + + + + +

    How Android Manages Memory

    + +

    Android does not offer swap space for memory, but it does use paging and memory-mapping +(mmapping) to manage memory. This means that any memory you modify—whether by allocating +new objects or touching mmapped pages—remains resident in RAM and cannot be paged out. +So the only way to completely release memory from your app is to release object references you may +be holding, making the memory available to the garbage collector. That is with one exception: +any files mmapped in without modification, such as code, can be paged out of RAM if the system +wants to use that memory elsewhere.

    + + +

    Sharing Memory

    + +

    In order to fit everything it needs in RAM, Android tries to share RAM pages across processes. It +can do so in the following ways:

    + + +

    Due to the extensive use of shared memory, determining how much memory your app is using requires +care. Techniques to properly determine your app's memory use are discussed in Investigating Your RAM Usage.

    + + +

    Allocating and Reclaiming App Memory

    + +

    Here are some facts about how Android allocates then reclaims memory from your app:

    + + + + +

    Restricting App Memory

    + +

    To maintain a functional multi-tasking environment, Android sets a hard limit on the heap size +for each app. The exact heap size limit varies between devices based on how much RAM the device +has available overall. If your app has reached the heap capacity and tries to allocate more +memory, it will receive an {@link java.lang.OutOfMemoryError}.

    + +

    In some cases, you might want to query the system to determine exactly how much heap space you +have available on the current device—for example, to determine how much data is safe to keep in a +cache. You can query the system for this figure by calling {@link +android.app.ActivityManager#getMemoryClass()}. This returns an integer indicating the number of +megabytes available for your app's heap. This is discussed further below, under +Check how much memory you should use.

    + + +

    Switching Apps

    + +

    Instead of using swap space when the user switches between apps, Android keeps processes that +are not hosting a foreground ("user visible") app component in a least-recently used (LRU) cache. +For example, when the user first launches an app, a process is created for it, but when the user +leaves the app, that process does not quit. The system keeps the process cached, so if +the user later returns to the app, the process is reused for faster app switching.

    + +

    If your app has a cached process and it retains memory that it currently does not need, +then your app—even while the user is not using it—is constraining the system's +overall performance. So, as the system runs low on memory, it may kill processes in the LRU cache +beginning with the process least recently used, but also giving some consideration toward +which processes are most memory intensive. To keep your process cached as long as possible, follow +the advice in the following sections about when to release your references.

    + +

    More information about how processes are cached while not running in the foreground and how +Android decides which ones +can be killed is available in the Processes and Threads guide.

    + + + + +

    How Your App Should Manage Memory

    + +

    You should consider RAM constraints throughout all phases of development, including during app +design (before you begin development). There are many +ways you can design and write code that lead to more efficient results, through aggregation of the +same techniques applied over and over.

    + +

    You should apply the following techniques while designing and implementing your app to make it +more memory efficient.

    + + +

    Use services sparingly

    + +

    If your app needs a service +to perform work in the background, do not keep it running unless +it's actively performing a job. Also be careful to never leak your service by failing to stop it +when its work is done.

    + +

    When you start a service, the system prefers to always keep the process for that service +running. This makes the process very expensive because the RAM used by the service can’t be used by +anything else or paged out. This reduces the number of cached processes that the system can keep in +the LRU cache, making app switching less efficient. It can even lead to thrashing in the system +when memory is tight and the system can’t maintain enough processes to host all the services +currently running.

    + +

    The best way to limit the lifespan of your service is to use an {@link +android.app.IntentService}, which finishes +itself as soon as it's done handling the intent that started it. For more information, read +Running in a Background Service +.

    + +

    Leaving a service running when it’s not needed is one of the worst memory-management +mistakes an Android app can make. So don’t be greedy by keeping a service for your app +running. Not only will it increase the risk of your app performing poorly due to RAM constraints, +but users will discover such misbehaving apps and uninstall them.

    + + +

    Release memory when your user interface becomes hidden

    + +

    When the user navigates to a different app and your UI is no longer visible, you should +release any resources that are used by only your UI. Releasing UI resources at this time can +significantly increase the system's capacity for cached processes, which has a direct impact on the +quality of the user experience.

    + +

    To be notified when the user exits your UI, implement the {@link +android.content.ComponentCallbacks2#onTrimMemory onTrimMemory()} callback in your {@link +android.app.Activity} classes. You should use this +method to listen for the {@link android.content.ComponentCallbacks2#TRIM_MEMORY_UI_HIDDEN} level, +which indicates your UI is now hidden from view and you should free resources that only your UI +uses.

    + + +

    Notice that your app receives the {@link android.content.ComponentCallbacks2#onTrimMemory +onTrimMemory()} callback with {@link android.content.ComponentCallbacks2#TRIM_MEMORY_UI_HIDDEN} +only when all the UI components of your app process become hidden from the user. +This is distinct +from the {@link android.app.Activity#onStop onStop()} callback, which is called when an {@link +android.app.Activity} instance becomes hidden, which occurs even when the user moves to +another activity in your app. So although you should implement {@link android.app.Activity#onStop +onStop()} to release activity resources such as a network connection or to unregister broadcast +receivers, you usually should not release your UI resources until you receive {@link +android.content.ComponentCallbacks2#onTrimMemory onTrimMemory(TRIM_MEMORY_UI_HIDDEN)}. This ensures +that if the user navigates back from another activity in your app, your UI resources are +still available to resume the activity quickly.

    + + + +

    Release memory as memory becomes tight

    + +

    During any stage of your app's lifecycle, the {@link +android.content.ComponentCallbacks2#onTrimMemory onTrimMemory()} callback also tells you when +the overall device memory is getting low. You should respond by further releasing resources based +on the following memory levels delivered by {@link android.content.ComponentCallbacks2#onTrimMemory +onTrimMemory()}:

    + + + +

    Also, when your app process is currently cached, you may receive one of the following +levels from {@link android.content.ComponentCallbacks2#onTrimMemory onTrimMemory()}:

    + + +

    Because the {@link android.content.ComponentCallbacks2#onTrimMemory onTrimMemory()} callback was +added in API level 14, you can use the {@link android.content.ComponentCallbacks#onLowMemory()} +callback as a fallback for older versions, which is roughly equivalent to the {@link +android.content.ComponentCallbacks2#TRIM_MEMORY_COMPLETE} event.

    + +

    Note: When the system begins killing processes in the LRU cache, +although it primarily works bottom-up, it does give some consideration to which processes are +consuming more memory and will thus provide the system more memory gain if killed. +So the less memory you consume while in the LRU list overall, the better your chances are +to remain in the list and be able to quickly resume.

    + + + +

    Check how much memory you should use

    + +

    As mentioned earlier, each Android-powered device has a different amount of RAM available to the +system and thus provides a different heap limit for each app. You can call {@link +android.app.ActivityManager#getMemoryClass()} to get an estimate of your app's available heap in +megabytes. If your app tries to allocate more memory than is available here, it will receive an +{@link java.lang.OutOfMemoryError}.

    + +

    In very special situations, you can request a larger heap size by setting the {@code largeHeap} +attribute to "true" in the manifest {@code <application>} +tag. If you do so, you can call {@link +android.app.ActivityManager#getLargeMemoryClass()} to get an estimate of the large heap size.

    + +

    However, the ability to request a large heap is intended only for a small set of apps that can +justify the need to consume more RAM (such as a large photo editing app). Never request a +large heap simply because you've run out of memory and you need a quick fix—you +should use it only when you know exactly where all your memory is being allocated and why it must +be retained. Yet, even when you're confident your app can justify the large heap, you should avoid +requesting it to whatever extent possible. Using the extra memory will increasingly be to the +detriment of the overall user experience because garbage collection will take longer and system +performance may be slower when task switching or performing other common operations.

    + +

    Additionally, the large heap size is not the same on all devices and, when running on +devices that have limited RAM, the large heap size may be exactly the same as the regular heap +size. So even if you do request the large heap size, you should call {@link +android.app.ActivityManager#getMemoryClass()} to check the regular heap size and strive to always +stay below that limit.

    + + +

    Avoid wasting memory with bitmaps

    + +

    When you load a bitmap, keep it in RAM only at the resolution you need for the current device's +screen, scaling it down if the original bitmap is a higher resolution. Keep in mind that an +increase in bitmap resolution results in a corresponding (increase2) in memory needed, +because both the X and Y dimensions increase.

    + +

    Note: On Android 2.3.x (API level 10) and below, bitmap objects +always appear as the same size in your app heap regardless of the image resolution (the actual +pixel data is stored separately in native memory). This makes it more difficult to debug the bitmap +memory allocation because most heap analysis tools do not see the native allocation. However, +beginning in Android 3.0 (API level 11), the bitmap pixel data is allocated in your app's Dalvik +heap, improving garbage collection and debuggability. So if your app uses bitmaps and you're having +trouble discovering why your app is using some memory on an older device, switch to a device +running Android 3.0 or higher to debug it.

    + +

    For more tips about working with bitmaps, read Managing Bitmap Memory.

    + + +

    Use optimized data containers

    + +

    Take advantage of optimized containers in the Android framework, such as {@link +android.util.SparseArray}, {@link android.util.SparseBooleanArray}, and {@link +android.support.v4.util.LongSparseArray}. The generic {@link java.util.HashMap} +implementation can be quite memory +inefficient because it needs a separate entry object for every mapping. Additionally, the {@link +android.util.SparseArray} classes are more efficient because they avoid the system's need +to autobox +the key and sometimes value (which creates yet another object or two per entry). And don't be +afraid of dropping down to raw arrays when that makes sense.

    + + + +

    Be aware of memory overhead

    + +

    Be knowledgeable about the cost and overhead of the language and libraries you are using, and +keep this information in mind when you design your app, from start to finish. Often, things on the +surface that look innocuous may in fact have a large amount of overhead. Examples include:

    + + +

    A few bytes here and there quickly add up—app designs that are class- or object-heavy will suffer +from this overhead. That can leave you in the difficult position of looking at a heap analysis and +realizing your problem is a lot of small objects using up your RAM.

    + + +

    Be careful with code abstractions

    + +

    Often, developers use abstractions simply as a "good programming practice," because abstractions +can improve code flexibility and maintenance. However, abstractions come at a significant cost: +generally they require a fair amount more code that needs to be executed, requiring more time and +more RAM for that code to be mapped into memory. So if your abstractions aren't supplying a +significant benefit, you should avoid them.

    + + +

    Use nano protobufs for serialized data

    + +

    Protocol +buffers are a language-neutral, platform-neutral, extensible mechanism designed by Google for +serializing structured data—think XML, but smaller, faster, and simpler. If you decide to use +protobufs for your data, you should always use nano protobufs in your client-side code. Regular +protobufs generate extremely verbose code, which will cause many kinds of problems in your app: +increased RAM use, significant APK size increase, slower execution, and quickly hitting the DEX +symbol limit.

    + +

    For more information, see the "Nano version" section in the protobuf readme.

    + + + +

    Avoid dependency injection frameworks

    + +

    Using a dependency injection framework such as Guice or +RoboGuice may be +attractive because they can simplify the code you write and provide an adaptive environment +that's useful for testing and other configuration changes. However, these frameworks tend to perform +a lot of process initialization by scanning your code for annotations, which can require significant +amounts of your code to be mapped into RAM even though you don't need it. These mapped pages are +allocated into clean memory so Android can drop them, but that won't happen until the pages have +been left in memory for a long period of time.

    + + +

    Be careful about using external libraries

    + +

    External library code is often not written for mobile environments and can be inefficient when used +for work on a mobile client. At the very least, when you decide to use an external library, you +should assume you are taking on a significant porting and maintenance burden to optimize the +library for mobile. Plan for that work up-front and analyze the library in terms of code size and +RAM footprint before deciding to use it at all.

    + +

    Even libraries supposedly designed for use on Android are potentially dangerous because each +library may do things differently. For example, one library may use nano protobufs while another +uses micro protobufs. Now you have two different protobuf implementations in your app. This can and +will also happen with different implementations of logging, analytics, image loading frameworks, +caching, and all kinds of other things you don't expect. ProGuard won't save you here because these +will all be lower-level dependencies that are required by the features for which you want the +library. This becomes especially problematic when you use an {@link android.app.Activity} +subclass from a library (which +will tend to have wide swaths of dependencies), when libraries use reflection (which is common and +means you need to spend a lot of time manually tweaking ProGuard to get it to work), and so on.

    + +

    Also be careful not to fall into the trap of using a shared library for one or two features out of +dozens of other things it does; you don't want to pull in a large amount of code and overhead that +you don't even use. At the end of the day, if there isn't an existing implementation that is a +strong match for what you need to do, it may be best if you create your own implementation.

    + + +

    Optimize overall performance

    + +

    A variety of information about optimizing your app's overall performance is available +in other documents listed in Best Practices +for Performance. Many of these documents include optimizations tips for CPU performance, but +many of these tips also help optimize your app's memory use, such as by reducing the number of +layout objects required by your UI.

    + +

    You should also read about optimizing +your UI with the layout debugging tools and take advantage of +the optimization suggestions provided by the lint tool.

    + + +

    Use ProGuard to strip out any unneeded code

    + +

    The ProGuard tool shrinks, +optimizes, and obfuscates your code by removing unused code and renaming classes, fields, and +methods with semantically obscure names. Using ProGuard can make your code more compact, requiring +fewer RAM pages to be mapped.

    + + +

    Use zipalign on your final APK

    + +

    If you do any post-processing of an APK generated by a build system (including signing it +with your final production certificate), then you must run zipalign on it to have it re-aligned. +Failing to do so can cause your app to require significantly more RAM, because things like +resources can no longer be mmapped from the APK.

    + +

    Note: Google Play Store does not accept APK files that +are not zipaligned.

    + + +

    Analyze your RAM usage

    + +

    Once you achieve a relatively stable build, begin analyzing how much RAM your app is using +throughout all stages of its lifecycle. For information about how to analyze your app, read Investigating Your RAM Usage.

    + + + + +

    Use multiple processes

    + +

    If it's appropriate for your app, an advanced technique that may help you manage your app's +memory is dividing components of your app into multiple processes. This technique must always be +used carefully and most apps should not run multiple processes, as it can easily +increase—rather than decrease—your RAM footprint if done incorrectly. It is primarily +useful to apps that may run significant work in the background as well as the foreground and can +manage those operations separately.

    + + +

    An example of when multiple processes may be appropriate is when building a music player that +plays music from a service for long period of time. If +the entire app runs in one process, then many of the allocations performed for its activity UI must +be kept around as long as it is playing music, even if the user is currently in another app and the +service is controlling the playback. An app like this may be split into two process: one for its +UI, and the other for the work that continues running in the background service.

    + +

    You can specify a separate process for each app component by declaring the {@code android:process} attribute +for each component in the manifest file. For example, you can specify that your service should run +in a process separate from your app's main process by declaring a new process named "background" +(but you can name the process anything you like):

    + +
    +<service android:name=".PlaybackService"
    +         android:process=":background" />
    +
    + +

    Your process name should begin with a colon (':') to ensure that the process remains private to +your app.

    + +

    Before you decide to create a new process, you need to understand the memory implications. +To illustrate the consequences of each process, consider that an empty process doing basically +nothing has an extra memory footprint of about 1.4MB, as shown by the memory information +dump below.

    + +
    +adb shell dumpsys meminfo com.example.android.apis:empty
    +
    +** MEMINFO in pid 10172 [com.example.android.apis:empty] **
    +                Pss     Pss  Shared Private  Shared Private    Heap    Heap    Heap
    +              Total   Clean   Dirty   Dirty   Clean   Clean    Size   Alloc    Free
    +             ------  ------  ------  ------  ------  ------  ------  ------  ------
    +  Native Heap     0       0       0       0       0       0    1864    1800      63
    +  Dalvik Heap   764       0    5228     316       0       0    5584    5499      85
    + Dalvik Other   619       0    3784     448       0       0
    +        Stack    28       0       8      28       0       0
    +    Other dev     4       0      12       0       0       4
    +     .so mmap   287       0    2840     212     972       0
    +    .apk mmap    54       0       0       0     136       0
    +    .dex mmap   250     148       0       0    3704     148
    +   Other mmap     8       0       8       8      20       0
    +      Unknown   403       0     600     380       0       0
    +        TOTAL  2417     148   12480    1392    4832     152    7448    7299     148
    +
    + +

    Note: More information about how to read this output is provided +in Investigating +Your RAM Usage. The key data here is the Private Dirty and Private +Clean memory, which shows that this process is using almost 1.4MB of non-pageable RAM +(distributed across the Dalvik heap, native allocations, book-keeping, and library-loading), +and another 150K of RAM for code that has been mapped in to execute.

    + +

    This memory footprint for an empty process is fairly significant and it can quickly +grow as you start doing work in that process. For +example, here is the memory use of a process that is created only to show an activity with some +text in it:

    + +
    +** MEMINFO in pid 10226 [com.example.android.helloactivity] **
    +                Pss     Pss  Shared Private  Shared Private    Heap    Heap    Heap
    +              Total   Clean   Dirty   Dirty   Clean   Clean    Size   Alloc    Free
    +             ------  ------  ------  ------  ------  ------  ------  ------  ------
    +  Native Heap     0       0       0       0       0       0    3000    2951      48
    +  Dalvik Heap  1074       0    4928     776       0       0    5744    5658      86
    + Dalvik Other   802       0    3612     664       0       0
    +        Stack    28       0       8      28       0       0
    +       Ashmem     6       0      16       0       0       0
    +    Other dev   108       0      24     104       0       4
    +     .so mmap  2166       0    2824    1828    3756       0
    +    .apk mmap    48       0       0       0     632       0
    +    .ttf mmap     3       0       0       0      24       0
    +    .dex mmap   292       4       0       0    5672       4
    +   Other mmap    10       0       8       8      68       0
    +      Unknown   632       0     412     624       0       0
    +        TOTAL  5169       4   11832    4032   10152       8    8744    8609     134
    +
    + +

    The process has now almost tripled in size, to 4MB, simply by showing some text in the UI. This +leads to an important conclusion: If you are going to split your app into multiple processes, only +one process should be responsible for UI. Other processes should avoid any UI, as this will quickly +increase the RAM required by the process (especially once you start loading bitmap assets and other +resources). It may then be hard or impossible to reduce the memory usage once the UI is drawn.

    + +

    Additionally, when running more than one process, it's more important than ever that you keep your +code as lean as possible, because any unnecessary RAM overhead for common implementations are now +replicated in each process. For example, if you are using enums (though you should not use enums), all of +the RAM needed to create and initialize those constants is duplicated in each process, and any +abstractions you have with adapters and temporaries or other overhead will likewise be replicated.

    + +

    Another concern with multiple processes is the dependencies that exist between them. For example, +if your app has a content provider that you have running in the default process which also hosts +your UI, then code in a background process that uses that content provider will also require that +your UI process remain in RAM. If your goal is to have a background process that can run +independently of a heavy-weight UI process, it can't have dependencies on content providers or +services that execute in the UI process.

    + + + + + + + + + + + diff --git a/docs/html/training/articles/perf-anr.jd b/docs/html/training/articles/perf-anr.jd index d3b2318..87cfc1c 100644 --- a/docs/html/training/articles/perf-anr.jd +++ b/docs/html/training/articles/perf-anr.jd @@ -8,7 +8,7 @@ page.article=true

    In this document

    -
      +
      1. What Triggers ANR?
      2. How to Avoid ANRs
      3. Reinforcing Responsiveness
      4. diff --git a/docs/html/training/articles/perf-jni.jd b/docs/html/training/articles/perf-jni.jd index 0d1f04e..9f880ec 100644 --- a/docs/html/training/articles/perf-jni.jd +++ b/docs/html/training/articles/perf-jni.jd @@ -8,7 +8,7 @@ page.article=true

        In this document

        -
          +
          1. JavaVM and JNIEnv
          2. Threads
          3. jclass, jmethodID, and jfieldID
          4. diff --git a/docs/html/training/articles/perf-tips.jd b/docs/html/training/articles/perf-tips.jd index f91ecd7..7ff6c5c 100644 --- a/docs/html/training/articles/perf-tips.jd +++ b/docs/html/training/articles/perf-tips.jd @@ -6,7 +6,7 @@ page.article=true

            In this document

            -
              +
              1. Avoid Creating Unnecessary Objects
              2. Prefer Static Over Virtual
              3. Use Static Final For Constants
              4. diff --git a/docs/html/training/training_toc.cs b/docs/html/training/training_toc.cs index 27e7004..22ebfe0 100644 --- a/docs/html/training/training_toc.cs +++ b/docs/html/training/training_toc.cs @@ -1062,6 +1062,14 @@ include the action bar on devices running Android 2.1 or higher."