Slide 1

Slide 1 text

Memories of Android (based on a talk and articles by Dianne Hackborn) Romain Guy Chet Haase google.com/+RomainGuy google.com/+ChetHaase @romainguy @chethaase

Slide 2

Slide 2 text

Why? • Android is different • Mobile is different • Many small decisions can create large problems • We all want more. Always.

Slide 3

Slide 3 text

Mobile Dynamics “The hardware will be faster next year”

Slide 4

Slide 4 text

Mobile Dynamics “The hardware will be faster next year” vs. “This year’s hardware will be cheaper next year”

Slide 5

Slide 5 text

Mobile Dynamics

Slide 6

Slide 6 text

Mobile Dynamics •We want an experience better than desktop

Slide 7

Slide 7 text

Mobile Dynamics •We want an experience better than desktop •On much slower hardware

Slide 8

Slide 8 text

Mobile Dynamics •We want an experience better than desktop •On much slower hardware •With higher resolution displays

Slide 9

Slide 9 text

Mobile Dynamics •We want an experience better than desktop •On much slower hardware •With higher resolution displays •On battery

Slide 10

Slide 10 text

Mobile Dynamics •We want an experience better than desktop •On much slower hardware •With higher resolution displays •On battery •For as long as possible

Slide 11

Slide 11 text

Android 2.3 • Still ships with many new [low-end] devices - Because of RAM

Slide 12

Slide 12 text

Agenda • Android and RAM • Guidelines • Measurement

Slide 13

Slide 13 text

Android and RAM 7

Slide 14

Slide 14 text

No Swap • Clean RAM - Paging of mmap’d files as needed • Dirty RAM - Can't swap out - Relatively expensive • Especially in background processes

Slide 15

Slide 15 text

Zygote • Process from which your app is spawned • Preloads framework classes • Preloads common assets • Preloads native libraries

Slide 16

Slide 16 text

Zygote Framework Assets Libraries App1 Classes Assets Libraries Shared Shared App2 Classes Assets Libraries Shared

Slide 17

Slide 17 text

Types of memory Dirty Clean Private Shared Bad Okay Good Best

Slide 18

Slide 18 text

Overcommit • Reserve address space for an allocation • Only mapped when needed • Allocations generally don’t fail • What happens when no RAM is available?

Slide 19

Slide 19 text

Processes • Instead of swap, Android uses processes* - Running vs. cached - Cached processes can be killed - Order of killing is LRU • with other modifications • Cached processes help Android user experience *See “Processes and Threads” in API Guides documentation

Slide 20

Slide 20 text

zRAM • New in Android 4.4 • Enabled on low-memory devices • Type of swap - Swap to compressed RAM

Slide 21

Slide 21 text

Shared Memory • Extensively used - Requires care in determining RAM use • Helps minimize memory footprint

Slide 22

Slide 22 text

Shared Memory • mmap - dalvik code, apk resources, ... - Share across processes, allows paging • zygote - First Dalvik process, from which all others fork - Preloads commonly used classes, resources - Common RAM shared across forked processes • ashmem - Large allocations shared instead of copied - Cursors, some graphics resources

Slide 23

Slide 23 text

Kernel SamePage Merging • New in Android 4.4 • Share identical pages between processes - Copy-on-write • Really useful with bitmaps - Bitmaps allocated on the Dalvik heap - byte[] allocations are zeroed out by the VM

Slide 24

Slide 24 text

How big is an Object? overhead of Object + overhead of dlmalloc + data

Slide 25

Slide 25 text

How big is an Object? overhead of Object + overhead of dlmalloc + data 8 bytes

Slide 26

Slide 26 text

How big is an Object? overhead of Object + overhead of dlmalloc + data 8 bytes 4-8 bytes

Slide 27

Slide 27 text

How big is an Object? overhead of Object + overhead of dlmalloc + data 8 bytes 4-8 bytes n bytes

Slide 28

Slide 28 text

How big is an Object? overhead of Object + overhead of dlmalloc + data 8 bytes 4-8 bytes n bytes The result must be 8-byte aligned

Slide 29

Slide 29 text

Size of data Type Size as field/variable Size in array Object reference 4 4 boolean 4 1 byte 4 1 char 4 2 short 4 2 int 4 4 float 4 4 long 8 8 double 8 8 All sizes in bytes

Slide 30

Slide 30 text

Object size examples

Slide 31

Slide 31 text

Object size examples class Empty { }

Slide 32

Slide 32 text

Object size examples class Empty { } Allocation Size in bytes dlmalloc 4 Object overhead 8

Slide 33

Slide 33 text

Object size examples class Empty { } Allocation Size in bytes dlmalloc 4 Object overhead 8 Total = 4 + 8 = 12 bytes

Slide 34

Slide 34 text

Object size examples class Empty { } Allocation Size in bytes dlmalloc 4 Object overhead 8 Total = 4 + 8 = 12 bytes 8-byte aligned total = 16 bytes

Slide 35

Slide 35 text

Object size examples class Integer { private int value; }

Slide 36

Slide 36 text

Object size examples class Integer { private int value; } Allocation Size in bytes dlmalloc 4 Object overhead 8

Slide 37

Slide 37 text

Object size examples class Integer { private int value; } Allocation Size in bytes dlmalloc 4 Object overhead 8 int 4

Slide 38

Slide 38 text

Object size examples class Integer { private int value; } Allocation Size in bytes dlmalloc 4 Object overhead 8 int 4 Total = 4 + 8 + 4 = 16 bytes

Slide 39

Slide 39 text

Object size examples class HashMap$HashMapEntry { final K key; V value; final int hash; HashMapEntry next; }

Slide 40

Slide 40 text

Object size examples Allocation Size in bytes dlmalloc 4 Object overhead 8 class HashMap$HashMapEntry { final K key; V value; final int hash; HashMapEntry next; }

Slide 41

Slide 41 text

Object size examples Allocation Size in bytes dlmalloc 4 Object overhead 8 Reference 4 Reference 4 int 4 Reference 4 class HashMap$HashMapEntry { final K key; V value; final int hash; HashMapEntry next; }

Slide 42

Slide 42 text

Object size examples Allocation Size in bytes dlmalloc 4 Object overhead 8 Reference 4 Reference 4 int 4 Reference 4 Total = 4 + 8 + 4 * 4 = 28 bytes class HashMap$HashMapEntry { final K key; V value; final int hash; HashMapEntry next; } Aligned total = 32 bytes

Slide 43

Slide 43 text

How big is an array? overhead of Object + overhead of dlmalloc + width + padding + data

Slide 44

Slide 44 text

How big is an array? overhead of Object + overhead of dlmalloc + width + padding + data 8 bytes

Slide 45

Slide 45 text

How big is an array? overhead of Object + overhead of dlmalloc + width + padding + data 8 bytes 4-8 bytes

Slide 46

Slide 46 text

How big is an array? overhead of Object + overhead of dlmalloc + width + padding + data 8 bytes 4-8 bytes 4 bytes

Slide 47

Slide 47 text

How big is an array? overhead of Object + overhead of dlmalloc + width + padding + data 8 bytes 4-8 bytes 4 bytes 4 bytes

Slide 48

Slide 48 text

How big is an array? overhead of Object + overhead of dlmalloc + width + padding + data 8 bytes 4-8 bytes 4 bytes 4 bytes n bytes

Slide 49

Slide 49 text

How big is an array? overhead of Object + overhead of dlmalloc + width + padding + data 8 bytes 4-8 bytes 4 bytes The result must be 8-byte aligned 4 bytes n bytes

Slide 50

Slide 50 text

Array size examples

Slide 51

Slide 51 text

Array size examples new byte[1]

Slide 52

Slide 52 text

Object size examples Allocation Size in bytes dlmalloc 4 Object overhead 8 width & padding 8 new byte[1]

Slide 53

Slide 53 text

Object size examples Allocation Size in bytes dlmalloc 4 Object overhead 8 width & padding 8 data 1 new byte[1]

Slide 54

Slide 54 text

Object size examples Allocation Size in bytes dlmalloc 4 Object overhead 8 width & padding 8 data 1 new byte[1] Total = 4 + 8 + 8 + 1 = 21 bytes 8-byte aligned total = 24 bytes

Slide 55

Slide 55 text

Objects vs primitive types

Slide 56

Slide 56 text

Objects vs primitive types Integer 16 bytes

Slide 57

Slide 57 text

Objects vs primitive types Integer 16 bytes int 4 bytes vs.

Slide 58

Slide 58 text

Objects vs primitive types Integer 16 bytes Boolean 16 bytes int 4 bytes vs.

Slide 59

Slide 59 text

Objects vs primitive types Integer 16 bytes Boolean 16 bytes int 4 bytes vs. boolean 4 bytes vs.

Slide 60

Slide 60 text

Objects vs primitive types Integer 16 bytes Boolean 16 bytes int 4 bytes vs. boolean 4 bytes vs. 1 bit bit-field vs.

Slide 61

Slide 61 text

Primitive types vs primitive types private boolean mProperty1; private boolean mProperty1; // … private boolean mProperty32; Total = 32 * 4= 128 bytes

Slide 62

Slide 62 text

Primitive types vs primitive types private boolean mProperties = new boolean[32]; Total = 4 + 8 + 8 + 32 * 1= 52 bytes Aligned total = 56 bytes

Slide 63

Slide 63 text

Primitive types vs primitive types private int mProperties; Total = 4 bytes // This is what we use in android.view.View

Slide 64

Slide 64 text

Classes • Inner class: ~500 bytes of code overhead button.setOnClickListener(new Runnable() { public void run() { // do stuff } });

Slide 65

Slide 65 text

Enums

Slide 66

Slide 66 text

Enums public static enum Things { THING_1, THING_2, }; +1,112 bytes dex file size

Slide 67

Slide 67 text

Enums public static int THING_1 = 1; public static int THING_2 = 2; vs. +128 bytes public static enum Things { THING_1, THING_2, }; +1,112 bytes dex file size

Slide 68

Slide 68 text

Enums vs. ints

Slide 69

Slide 69 text

Enums vs. ints public static enum Things { THING_1, THING_2, };

Slide 70

Slide 70 text

Enums vs. ints .class public final enum LThings; .super Ljava/lang/Enum; .source "Things.java" .annotation system Ldalvik/annotation/Signature; value = { "Ljava/lang/Enum", "<", "LThings;", ">;" } .end annotation .field private static final synthetic $VALUES:[LThings; .field public static final enum THING_1:LThings; .field public static final enum THING_2:LThings; .method static constructor ()V .registers 4 const/4 v3, 0x1 const/4 v2, 0x0 new-instance v0, LThings; const-string v1, "THING_1" invoke-direct {v0, v1, v2}, LThings;->(Ljava/lang/String;I)V sput-object v0, LThings;->THING_1:LThings; new-instance v0, LThings; const-string v1, "THING_2" invoke-direct {v0, v1, v3}, LThings;->(Ljava/lang/String;I)V sput-object v0, LThings;->THING_2:LThings; const/4 v0, 0x2 new-array v0, v0, [LThings; sget-object v1, LThings;->THING_1:LThings; aput-object v1, v0, v2 sget-object v1, LThings;->THING_2:LThings; aput-object v1, v0, v3 sput-object v0, LThings;->$VALUES:[LThings; return-void .end method .method private constructor (Ljava/lang/String;I)V .registers 3 .annotation system Ldalvik/annotation/Signature; value = { "()V" } .end annotation invoke-direct {p0, p1, p2}, Ljava/lang/Enum;->(Ljava/lang/String;I)V return-void .end method .method public static valueOf(Ljava/lang/String;)LThings; .registers 2 .param p0 # Ljava/lang/String; const-class v0, LThings; invoke-static {v0, p0}, Ljava/lang/Enum;->valueOf(Ljava/lang/Class;Ljava/lang/String;)Ljava/lang/Enum; move-result-object v0 check-cast v0, LThings; return-object v0 .end method .method public static values()[LThings; .registers 1 sget-object v0, LThings;->$VALUES:[LThings; invoke-virtual {v0}, [LThings;->clone()Ljava/lang/Object; move-result-object v0 check-cast v0, [LThings; return-object v0 .end method

Slide 71

Slide 71 text

Enums vs. ints .class public final enum LThings; .super Ljava/lang/Enum; .source "Things.java" .annotation system Ldalvik/annotation/Signature; value = { "Ljava/lang/Enum", "<", "LThings;", ">;" } .end annotation .field private static final synthetic $VALUES:[LThings; .field public static final enum THING_1:LThings; .field public static final enum THING_2:LThings; .method static constructor ()V .registers 4 const/4 v3, 0x1 const/4 v2, 0x0 new-instance v0, LThings; const-string v1, "THING_1" invoke-direct {v0, v1, v2}, LThings;->(Ljava/lang/String;I)V sput-object v0, LThings;->THING_1:LThings; new-instance v0, LThings; const-string v1, "THING_2" invoke-direct {v0, v1, v3}, LThings;->(Ljava/lang/String;I)V sput-object v0, LThings;->THING_2:LThings; const/4 v0, 0x2 new-array v0, v0, [LThings; sget-object v1, LThings;->THING_1:LThings; aput-object v1, v0, v2 sget-object v1, LThings;->THING_2:LThings; aput-object v1, v0, v3 sput-object v0, LThings;->$VALUES:[LThings; return-void .end method .method private constructor (Ljava/lang/String;I)V .registers 3 .annotation system Ldalvik/annotation/Signature; value = { "()V" } .end annotation invoke-direct {p0, p1, p2}, Ljava/lang/Enum;->(Ljava/lang/String;I)V return-void .end method .method public static valueOf(Ljava/lang/String;)LThings; .registers 2 .param p0 # Ljava/lang/String; const-class v0, LThings; invoke-static {v0, p0}, Ljava/lang/Enum;->valueOf(Ljava/lang/Class;Ljava/lang/String;)Ljava/lang/Enum; move-result-object v0 check-cast v0, LThings; return-object v0 .end method .method public static values()[LThings; .registers 1 sget-object v0, LThings;->$VALUES:[LThings; invoke-virtual {v0}, [LThings;->clone()Ljava/lang/Object; move-result-object v0 check-cast v0, [LThings; return-object v0 .end method public static int THING_1 = 1; public static int THING_2 = 2;

Slide 72

Slide 72 text

Enums vs. ints .class public final enum LThings; .super Ljava/lang/Enum; .source "Things.java" .annotation system Ldalvik/annotation/Signature; value = { "Ljava/lang/Enum", "<", "LThings;", ">;" } .end annotation .field private static final synthetic $VALUES:[LThings; .field public static final enum THING_1:LThings; .field public static final enum THING_2:LThings; .method static constructor ()V .registers 4 const/4 v3, 0x1 const/4 v2, 0x0 new-instance v0, LThings; const-string v1, "THING_1" invoke-direct {v0, v1, v2}, LThings;->(Ljava/lang/String;I)V sput-object v0, LThings;->THING_1:LThings; new-instance v0, LThings; const-string v1, "THING_2" invoke-direct {v0, v1, v3}, LThings;->(Ljava/lang/String;I)V sput-object v0, LThings;->THING_2:LThings; const/4 v0, 0x2 new-array v0, v0, [LThings; sget-object v1, LThings;->THING_1:LThings; aput-object v1, v0, v2 sget-object v1, LThings;->THING_2:LThings; aput-object v1, v0, v3 sput-object v0, LThings;->$VALUES:[LThings; return-void .end method .method private constructor (Ljava/lang/String;I)V .registers 3 .annotation system Ldalvik/annotation/Signature; value = { "()V" } .end annotation invoke-direct {p0, p1, p2}, Ljava/lang/Enum;->(Ljava/lang/String;I)V return-void .end method .method public static valueOf(Ljava/lang/String;)LThings; .registers 2 .param p0 # Ljava/lang/String; const-class v0, LThings; invoke-static {v0, p0}, Ljava/lang/Enum;->valueOf(Ljava/lang/Class;Ljava/lang/String;)Ljava/lang/Enum; move-result-object v0 check-cast v0, LThings; return-object v0 .end method .method public static values()[LThings; .registers 1 sget-object v0, LThings;->$VALUES:[LThings; invoke-virtual {v0}, [LThings;->clone()Ljava/lang/Object; move-result-object v0 check-cast v0, [LThings; return-object v0 .end method const/4 v0, 0x0 sput v0, LThings;->THING_1:I sput v0, LThings;->THING_2:I

Slide 73

Slide 73 text

Enums • Allocate more memory - Each value is an instance of the enum class • Execute more code - Class initializer runs when enum is loaded - Instantiates each value

Slide 74

Slide 74 text

Garbage isn’t Free • Temporary objects can also hurt

Slide 75

Slide 75 text

Garbage isn’t Free • Temporary objects can also hurt Integer width = view.getWidth();

Slide 76

Slide 76 text

Garbage isn’t Free • Temporary objects can also hurt Integer width = view.getWidth(); Autoboxing

Slide 77

Slide 77 text

Garbage isn’t Free • Temporary objects can also hurt Integer width = view.getWidth(); Autoboxing for (MyListener listener : mListeners) { // ... }

Slide 78

Slide 78 text

Garbage isn’t Free • Temporary objects can also hurt Integer width = view.getWidth(); Autoboxing for (MyListener listener : mListeners) { // ... } Iterator created

Slide 79

Slide 79 text

Guidelines 48

Slide 80

Slide 80 text

Beware Services • Very expensive • Need to stay running • Directly reduce available cached processes • Remember, no swap • Services should have well-defined durations • Services left running is a common application problem

Slide 81

Slide 81 text

Release your RAM public void onTrimMemory(int level) { // cached activity if (level >= TRIM_MEMORY_COMPLETE) { // ... } else if (level >= TRIM_MEMORY_MODERATE) { // ... } else if (level >= TRIM_MEMORY_BACKGROUND) { // ... } else if (level >= TRIM_MEMORY_UI_HIDDEN) { // ... } else if (level >= TRIM_MEMORY_RUNNING_CRITICAL) { // ... } else if (level >= TRIM_MEMORY_RUNNING_LOW) { // ... } else if (level >= TRIM_MEMORY_RUNNING_MODERATE) { // ... } } Cached Running

Slide 82

Slide 82 text

Memory Class ActivityManager.getMemoryClass(); ActivityManager.getLargeMemoryClass();

Slide 83

Slide 83 text

Bitmaps • Often largest RAM user • RAM size = width * height * depth - Optimize for size • Take care with caches of bitmaps • Android 3.0: bitmaps in Dalvik heap - Reuse when possible • See: http://developer.android.com/training/displaying-bitmaps/ manage-memory.html

Slide 84

Slide 84 text

ProGuard and Zipalign • Part of standard build tools • Use them

Slide 85

Slide 85 text

Design Guidelines • App design affects RAM usage • Harder to fix later • Common programming practices can be less memory efficient

Slide 86

Slide 86 text

Know your (Java) programming language • Java has many challenges for memory use • Have a general sense of the overhead of language features • Easier to write efficiently the first time

Slide 87

Slide 87 text

Abstractions • Hidden costs

Slide 88

Slide 88 text

External Libraries • Not necessarily written for Android • Potentially large expensive for small benefit

Slide 89

Slide 89 text

Android Libraries • Still significant overhead, duplication

Slide 90

Slide 90 text

Use Optimized Containers • Sparse arrays - Replace hash maps when the key is a primitive type - Variants for different key/value types • Benefits - Allocation-free - No boxing

Slide 91

Slide 91 text

Sparse arrays HashMap Array class SparseArray SparseBooleanArray SparseIntArray SparseLongArray LongSparseArray LongSparseLongArray* * Not a public class, copy from Android’s source code

Slide 92

Slide 92 text

Sparse arrays vs HashMap • SparseIntArray vs HashMap for 1,000 elements

Slide 93

Slide 93 text

Sparse arrays vs HashMap • SparseIntArray vs HashMap for 1,000 elements class SparseIntArray { int[] keys; int[] values; int size; }

Slide 94

Slide 94 text

Sparse arrays vs HashMap • SparseIntArray vs HashMap for 1,000 elements class SparseIntArray { int[] keys; int[] values; int size; } Class = 12 + 3 * 4 = 24 bytes Array = 20 + 1000 * 4 = 4024 bytes Total = 8,072 bytes

Slide 95

Slide 95 text

Sparse arrays vs HashMap • SparseIntArray vs HashMap for 1,000 elements

Slide 96

Slide 96 text

Sparse arrays vs HashMap • SparseIntArray vs HashMap for 1,000 elements class HashMap { Entry[] table; Entry forNull; int size; int modCount; int threshold; Set keys; Set> entries; Collection values; }

Slide 97

Slide 97 text

Sparse arrays vs HashMap • SparseIntArray vs HashMap for 1,000 elements class HashMap { Entry[] table; Entry forNull; int size; int modCount; int threshold; Set keys; Set> entries; Collection values; } Class = 12 + 8 * 4 = 48 bytes Array = 20 + 1000 * 64 = 64024 bytes Total = 64,136 bytes Entry = 32 + 16 + 16 = 64 bytes

Slide 98

Slide 98 text

Use Optimized Containers • ArrayMap - Replaces HashMap • Benefit - Allocation-free - Same API as HashMap (implements Map interface) - Available in support library • Drawback - Slower than HashMap, don’t use for large collections

Slide 99

Slide 99 text

Use Optimized Containers • Raw arrays - When it makes sense • android.view.ViewGroup - Children stored in a View[]

Slide 100

Slide 100 text

Measurement 65

Slide 101

Slide 101 text

Process Memory • USS (Unique Set Size) - Private Clean + Private Dirty - RAM committed to only that process • PSS (Proportional Set Size) - USS + memory shared with other processes

Slide 102

Slide 102 text

Meminfo Applications Memory Usage (kB): Uptime: 27233364 Realtime: 252885787 ** MEMINFO in pid 15976 [com.android.systemui] ** Pss Private Private Swapped Heap Heap Heap Total Dirty Clean Dirty Size Alloc Free ------ ------ ------ ------ ------ ------ ------ Native Heap 5308 5280 0 0 16172 7658 741 Dalvik Heap 7015 6684 0 0 19288 13124 6164 Dalvik Other 3328 3184 0 0 Stack 188 188 0 0 Ashmem 2 0 0 0 Other dev 4648 4356 4 0 .so mmap 1296 404 20 0 .apk mmap 1014 0 564 0 .ttf mmap 299 0 200 0 .dex mmap 1904 60 1132 0 Other mmap 100 4 44 0 Graphics 7904 7904 0 0 GL 15916 15916 0 0 Unknown 120 120 0 0 TOTAL 49042 44100 1964 0 35460 20782 6905

Slide 103

Slide 103 text

Meminfo Applications Memory Usage (kB): Uptime: 27233364 Realtime: 252885787 ** MEMINFO in pid 15976 [com.android.systemui] ** Pss Private Private Swapped Heap Heap Heap Total Dirty Clean Dirty Size Alloc Free ------ ------ ------ ------ ------ ------ ------ Native Heap 5308 5280 0 0 16172 7658 741 Dalvik Heap 7015 6684 0 0 19288 13124 6164 Dalvik Other 3328 3184 0 0 Stack 188 188 0 0 Ashmem 2 0 0 0 Other dev 4648 4356 4 0 .so mmap 1296 404 20 0 .apk mmap 1014 0 564 0 .ttf mmap 299 0 200 0 .dex mmap 1904 60 1132 0 Other mmap 100 4 44 0 Graphics 7904 7904 0 0 GL 15916 15916 0 0 Unknown 120 120 0 0 TOTAL 49042 44100 1964 0 35460 20782 6905

Slide 104

Slide 104 text

Meminfo Applications Memory Usage (kB): Uptime: 27233364 Realtime: 252885787 ** MEMINFO in pid 15976 [com.android.systemui] ** Pss Private Private Swapped Heap Heap Heap Total Dirty Clean Dirty Size Alloc Free ------ ------ ------ ------ ------ ------ ------ Native Heap 5308 5280 0 0 16172 7658 741 Dalvik Heap 7015 6684 0 0 19288 13124 6164 Dalvik Other 3328 3184 0 0 Stack 188 188 0 0 Ashmem 2 0 0 0 Other dev 4648 4356 4 0 .so mmap 1296 404 20 0 .apk mmap 1014 0 564 0 .ttf mmap 299 0 200 0 .dex mmap 1904 60 1132 0 Other mmap 100 4 44 0 Graphics 7904 7904 0 0 GL 15916 15916 0 0 Unknown 120 120 0 0 TOTAL 49042 44100 1964 0 35460 20782 6905

Slide 105

Slide 105 text

Meminfo Applications Memory Usage (kB): Uptime: 27233364 Realtime: 252885787 ** MEMINFO in pid 15976 [com.android.systemui] ** Pss Private Private Swapped Heap Heap Heap Total Dirty Clean Dirty Size Alloc Free ------ ------ ------ ------ ------ ------ ------ Native Heap 5308 5280 0 0 16172 7658 741 Dalvik Heap 7015 6684 0 0 19288 13124 6164 Dalvik Other 3328 3184 0 0 Stack 188 188 0 0 Ashmem 2 0 0 0 Other dev 4648 4356 4 0 .so mmap 1296 404 20 0 .apk mmap 1014 0 564 0 .ttf mmap 299 0 200 0 .dex mmap 1904 60 1132 0 Other mmap 100 4 44 0 Graphics 7904 7904 0 0 GL 15916 15916 0 0 Unknown 120 120 0 0 TOTAL 49042 44100 1964 0 35460 20782 6905

Slide 106

Slide 106 text

Meminfo Applications Memory Usage (kB): Uptime: 27233364 Realtime: 252885787 ** MEMINFO in pid 15976 [com.android.systemui] ** Pss Private Private Swapped Heap Heap Heap Total Dirty Clean Dirty Size Alloc Free ------ ------ ------ ------ ------ ------ ------ Native Heap 5308 5280 0 0 16172 7658 741 Dalvik Heap 7015 6684 0 0 19288 13124 6164 Dalvik Other 3328 3184 0 0 Stack 188 188 0 0 Ashmem 2 0 0 0 Other dev 4648 4356 4 0 .so mmap 1296 404 20 0 .apk mmap 1014 0 564 0 .ttf mmap 299 0 200 0 .dex mmap 1904 60 1132 0 Other mmap 100 4 44 0 Graphics 7904 7904 0 0 GL 15916 15916 0 0 Unknown 120 120 0 0 TOTAL 49042 44100 1964 0 35460 20782 6905

Slide 107

Slide 107 text

Meminfo Applications Memory Usage (kB): Uptime: 27233364 Realtime: 252885787 ** MEMINFO in pid 15976 [com.android.systemui] ** Pss Private Private Swapped Heap Heap Heap Total Dirty Clean Dirty Size Alloc Free ------ ------ ------ ------ ------ ------ ------ Native Heap 5308 5280 0 0 16172 7658 741 Dalvik Heap 7015 6684 0 0 19288 13124 6164 Dalvik Other 3328 3184 0 0 Stack 188 188 0 0 Ashmem 2 0 0 0 Other dev 4648 4356 4 0 .so mmap 1296 404 20 0 .apk mmap 1014 0 564 0 .ttf mmap 299 0 200 0 .dex mmap 1904 60 1132 0 Other mmap 100 4 44 0 Graphics 7904 7904 0 0 GL 15916 15916 0 0 Unknown 120 120 0 0 TOTAL 49042 44100 1964 0 35460 20782 6905

Slide 108

Slide 108 text

Meminfo (2) Objects Views: 288 ViewRootImpl: 5 AppContexts: 12 Activities: 1 Assets: 6 AssetManagers: 6 Local Binders: 66 Proxy Binders: 47 Death Recipients: 2 OpenSSL Sockets: 0

Slide 109

Slide 109 text

Meminfo (2) Objects Views: 288 ViewRootImpl: 5 AppContexts: 12 Activities: 1 Assets: 6 AssetManagers: 6 Local Binders: 66 Proxy Binders: 47 Death Recipients: 2 OpenSSL Sockets: 0

Slide 110

Slide 110 text

Meminfo (2) Objects Views: 288 ViewRootImpl: 5 AppContexts: 12 Activities: 1 Assets: 6 AssetManagers: 6 Local Binders: 66 Proxy Binders: 47 Death Recipients: 2 OpenSSL Sockets: 0

Slide 111

Slide 111 text

Meminfo (2) Objects Views: 288 ViewRootImpl: 5 AppContexts: 12 Activities: 1 Assets: 6 AssetManagers: 6 Local Binders: 66 Proxy Binders: 47 Death Recipients: 2 OpenSSL Sockets: 0

Slide 112

Slide 112 text

Meminfo (3) -a ** MEMINFO in pid 15976 [com.android.systemui] ** Pss Pss Shared Private Shared Private Swapped Total Clean Dirty Dirty Clean Clean Dirty ------ ------ ------ ------ ------ ------ ------ Native Heap 5308 0 780 5280 0 0 0 Dalvik Heap 7031 0 7484 6700 0 0 0 Dalvik Other 3332 0 3204 3188 0 0 0 Stack 188 0 8 188 0 0 0 Ashmem 2 0 4 0 0 0 0 Other dev 4648 0 604 4356 0 4 0 .so mmap 1296 20 2692 404 6620 20 0 .apk mmap 1014 564 0 0 1536 564 0 .ttf mmap 299 200 0 0 388 200 0 .dex mmap 1904 1132 416 60 8304 1132 0 Other mmap 100 0 12 4 332 44 0 Graphics 7904 0 0 7904 0 0 0 GL 15916 0 0 15916 0 0 0 Unknown 120 0 4 120 0 0 0 TOTAL 49062 1916 15208 44120 17180 1964 0

Slide 113

Slide 113 text

Meminfo (3) -a ** MEMINFO in pid 15976 [com.android.systemui] ** Pss Pss Shared Private Shared Private Swapped Total Clean Dirty Dirty Clean Clean Dirty ------ ------ ------ ------ ------ ------ ------ Native Heap 5308 0 780 5280 0 0 0 Dalvik Heap 7031 0 7484 6700 0 0 0 Dalvik Other 3332 0 3204 3188 0 0 0 Stack 188 0 8 188 0 0 0 Ashmem 2 0 4 0 0 0 0 Other dev 4648 0 604 4356 0 4 0 .so mmap 1296 20 2692 404 6620 20 0 .apk mmap 1014 564 0 0 1536 564 0 .ttf mmap 299 200 0 0 388 200 0 .dex mmap 1904 1132 416 60 8304 1132 0 Other mmap 100 0 12 4 332 44 0 Graphics 7904 0 0 7904 0 0 0 GL 15916 0 0 15916 0 0 0 Unknown 120 0 4 120 0 0 0 TOTAL 49062 1916 15208 44120 17180 1964 0

Slide 114

Slide 114 text

Exercise // 1MB + some overhead class Chunk { byte[] padding = new byte[1024 * 1024]; }

Slide 115

Slide 115 text

Exercise List mRetainedChunks = new ArrayList(); List mTempChunks = new ArrayList(); int i = 0; Runtime runtime = Runtime.getRuntime(); long max = runtime.maxMemory() - 1024 * 1024; while (runtime.totalMemory() < max) { ((i++ % 2 == 0) ? mRetainedChunks : mTempChunks) .add(new Chunk()); }

Slide 116

Slide 116 text

Exercise Log.d("Heap", "max=" + toMB(runtime.maxMemory())); Log.d("Heap", String.format("heap: %.2f/%.2f", toMB(runtime.freeMemory()), toMB(runtime.totalMemory())));

Slide 117

Slide 117 text

Exercise Log.d("Heap", "max=" + toMB(runtime.maxMemory())); Log.d("Heap", String.format("heap: %.2f/%.2f", toMB(runtime.freeMemory()), toMB(runtime.totalMemory()))); # < 1 MB of free memory D/Heap(13055): max=192.0 D/Heap(13055): heap: 0.84/191.99

Slide 118

Slide 118 text

Exercise // Remove half the chunks mTransientChunks.clear(); // Force a GC to free up memory System.gc();

Slide 119

Slide 119 text

Exercise // Remove half the chunks mTransientChunks.clear(); // Force a GC to free up memory System.gc(); # < 87 MB of free memory D/Heap(13055): heap: 87.86/191.99

Slide 120

Slide 120 text

Exercise // Allocate ~2MB Bitmap b = Bitmap.createBitmap(1024, 512, Bitmap.Config.ARGB_8888);

Slide 121

Slide 121 text

Exercise // Allocate ~2MB Bitmap b = Bitmap.createBitmap(1024, 512, Bitmap.Config.ARGB_8888); D/dalvikvm: GC_BEFORE_OOM freed 0K, 46% free 106633K/196600K, … E/dalvikvm-heap: Out of memory on a 2097168-byte allocation.

Slide 122

Slide 122 text

Dalvik Heap Management • Single virtual memory range • Non-compacting - The heap will fragment! • Can shrink if unused space at end of range • madvise used to free individual pages inside of the range - returns memory to kernel • even if the heap size does not shrink

Slide 123

Slide 123 text

Dalvik Heap Analysis • Zygote allocations are generally not of concern for an app • Convert data prior to heap analysis: hprof-conv

Slide 124

Slide 124 text

Collect Heap Data • Run your app • Select your app in DDMS • Press "Dump HPROF File" button • Save file

Slide 125

Slide 125 text

Analyze with [jh|m]at • localhost:7000 in browser • or load into mat $ hprof-conv ~/systemui.hprof ~/systemui-conv.hprof $ jhat ~/systemui-conv.hprof

Slide 126

Slide 126 text

Finding Leaks • Simple way: - 1. Run app for a while - 2. Look at heap - 3. Profit! • Caveats: - Use “adb shell dumpsys meminfo ” for initial overview - Finding large leaks is easy (sort by size) - finding systemic memory problems is often hard

Slide 127

Slide 127 text

Dalvik Allocation Tracker • Allocations over a set period of time • DDMS allocation tracker: - Select app - “Start tracking” - Interact with app - “Get allocations” - Click to see stack • Good tool for jank, too!

Slide 128

Slide 128 text

Dalvik Allocation Tracker • Allocations over a set period of time • DDMS allocation tracker: - Select app - “Start tracking” - Interact with app - “Get allocations” - Click to see stack • Good tool for jank, too! Great

Slide 129

Slide 129 text

Processes • Every process has overhead - Empty, do-nothing process: 1.5 MB USS - Ready to show UI: 4 MB USS - Showing UI: much more • Avoid multiple processes in general • Possible to run multiple apps in one process - Activity’s android:process attribute

Slide 130

Slide 130 text

procstats: UI

Slide 131

Slide 131 text

procstats: command line $ adb shell dumpsys procstats com.google.android.apps.maps COMMITTED STATS FROM 2013-11-05-18-04-58: * com.google.android.apps.maps / u0a60: TOTAL: 1.1% Service: 1.1% (Cached): 99% (98MB-98MB-99MB/96MB-97MB-97MB over 7) Run time Stats: Screen Off / Norm / +1h19m25s22ms Screen On / Norm / +10m43s963ms TOTAL: +1h30m8s985ms Start time: 2013-11-05 18:04:58 Total elapsed time: +5h9m53s44ms (complete) libdvm.so chromeview

Slide 132

Slide 132 text

For More Information • Managing Your App’s Memory - http://developer.android.com/training/articles/memory.html