1# Memory: Java heap profiler 2 3NOTE: The Java heap profiler requires Android 11 or higher 4 5See the [Memory Guide](/docs/case-studies/memory.md#java-hprof) for getting 6started with Java heap profiling. 7 8Conversely from the [Native heap profiler](native-heap-profiler.md), the Java 9heap profiler reports full retention graphs of managed objects but not 10call-stacks. The information recorded by the Java heap profiler is of the form: 11_Object X retains object Y, which is N bytes large, through its class member 12named Z_. 13 14## UI 15 16Heap graph dumps are shown as flamegraphs in the UI after clicking on the 17diamond in the _"Heap Profile"_ track of a process. Each diamond corresponds to 18a heap dump. 19 20 21 22 23 24The native size of certain objects is represented as an extra child node in the 25flamegraph, prefixed with "[native]". The extra node counts as an extra object. 26This is available only on Android T+. 27 28## SQL 29 30Information about the Java Heap is written to the following tables: 31 32* [`heap_graph_class`](/docs/analysis/sql-tables.autogen#heap_graph_class) 33* [`heap_graph_object`](/docs/analysis/sql-tables.autogen#heap_graph_object) 34* [`heap_graph_reference`](/docs/analysis/sql-tables.autogen#heap_graph_reference) 35 36`native_size` (available only on Android T+) is extracted from the related 37`libcore.util.NativeAllocationRegistry` and is not included in `self_size`. 38 39For instance, to get the bytes used by class name, run the following query. 40As-is this query will often return un-actionable information, as most of the 41bytes in the Java heap end up being primitive arrays or strings. 42 43```sql 44select c.name, sum(o.self_size) 45 from heap_graph_object o join heap_graph_class c on (o.type_id = c.id) 46 where reachable = 1 group by 1 order by 2 desc; 47``` 48 49|name |sum(o.self_size) | 50|--------------------|--------------------| 51|java.lang.String | 2770504| 52|long[] | 1500048| 53|int[] | 1181164| 54|java.lang.Object[] | 624812| 55|char[] | 357720| 56|byte[] | 350423| 57 58We can use `experimental_flamegraph` to normalize the graph into a tree, always 59taking the shortest path to the root and get cumulative sizes. 60Note that this is **experimental** and the **API is subject to change**. 61From this we can see how much memory is being held by each type of object 62 63For that, we need to find the timestamp and upid of the graph. 64 65```sql 66select distinct graph_sample_ts, upid from heap_graph_object 67``` 68 69graph_sample_ts | upid | 70--------------------|--------------------| 71 56785646801 | 1 | 72 73We can then use them to get the flamegraph data. 74 75```sql 76select name, cumulative_size 77 from experimental_flamegraph 78 where ts = 56785646801 79 and upid = 1 80 and profile_type = 'graph' 81 order by 2 desc; 82``` 83 84| name | cumulative_size | 85|------|-----------------| 86|java.lang.String|1431688| 87|java.lang.Class<android.icu.text.Transliterator>|1120227| 88|android.icu.text.TransliteratorRegistry|1119600| 89|com.android.systemui.statusbar.phone.StatusBarNotificationPresenter$2|1086209| 90|com.android.systemui.statusbar.phone.StatusBarNotificationPresenter|1085593| 91|java.util.Collections$SynchronizedMap|1063376| 92|java.util.HashMap|1063292| 93 94## TraceConfig 95 96The Java heap profiler is configured through the 97[JavaHprofConfig](/docs/reference/trace-config-proto.autogen#JavaHprofConfig) 98section of the trace config. 99 100```protobuf 101data_sources { 102 config { 103 name: "android.java_hprof" 104 java_hprof_config { 105 process_cmdline: "com.google.android.inputmethod.latin" 106 dump_smaps: true 107 } 108 } 109} 110``` 111