# Memory: Java heap profiler

NOTE: The Java heap profiler requires Android 11 or higher

See the [Memory Guide](/docs/case-studies/memory.md#java-hprof) for getting
started with Java heap profiling.

Conversely from the [Native heap profiler](native-heap-profiler.md), the Java
heap profiler reports full retention graphs of managed objects but not
call-stacks. The information recorded by the Java heap profiler is of the form:
_Object X retains object Y, which is N bytes large, through its class member
named Z_.

## UI

Heap graph dumps are shown as flamegraphs in the UI after clicking on the
diamond in the _"Heap Profile"_ track of a process. Each diamond corresponds to
a heap dump.

![Java heap profiles in the process tracks](/docs/images/profile-diamond.png)

![Flamegraph of a Java heap profiler](/docs/images/java-flamegraph.png)

The native size of certain objects is represented as an extra child node in the
flamegraph, prefixed with "[native]". The extra node counts as an extra object.
This is available only on Android T+.

## SQL

Information about the Java Heap is written to the following tables:

* [`heap_graph_class`](/docs/analysis/sql-tables.autogen#heap_graph_class)
* [`heap_graph_object`](/docs/analysis/sql-tables.autogen#heap_graph_object)
* [`heap_graph_reference`](/docs/analysis/sql-tables.autogen#heap_graph_reference)

`native_size` (available only on Android T+) is extracted from the related
`libcore.util.NativeAllocationRegistry` and is not included in `self_size`.

For instance, to get the bytes used by class name, run the following query.
As-is this query will often return un-actionable information, as most of the
bytes in the Java heap end up being primitive arrays or strings.

```sql
select c.name, sum(o.self_size)
       from heap_graph_object o join heap_graph_class c on (o.type_id = c.id)
       where reachable = 1 group by 1 order by 2 desc;
```

|name                |sum(o.self_size)    |
|--------------------|--------------------|
|java.lang.String    |             2770504|
|long[]              |             1500048|
|int[]               |             1181164|
|java.lang.Object[]  |              624812|
|char[]              |              357720|
|byte[]              |              350423|

We can use `experimental_flamegraph` to normalize the graph into a tree, always
taking the shortest path to the root and get cumulative sizes.
Note that this is **experimental** and the **API is subject to change**.
From this we can see how much memory is being held by each type of object

For that, we need to find the timestamp and upid of the graph.

```sql
select distinct graph_sample_ts, upid from heap_graph_object
```

graph_sample_ts     |        upid        |
--------------------|--------------------|
     56785646801    |         1          |

We can then use them to get the flamegraph data.

```sql
select name, cumulative_size
       from experimental_flamegraph
       where ts = 56785646801
            and upid = 1
            and profile_type = 'graph'
       order by 2 desc;
```

| name | cumulative_size |
|------|-----------------|
|java.lang.String|1431688|
|java.lang.Class<android.icu.text.Transliterator>|1120227|
|android.icu.text.TransliteratorRegistry|1119600|
|com.android.systemui.statusbar.phone.StatusBarNotificationPresenter$2|1086209|
|com.android.systemui.statusbar.phone.StatusBarNotificationPresenter|1085593|
|java.util.Collections$SynchronizedMap|1063376|
|java.util.HashMap|1063292|

## TraceConfig

The Java heap profiler is configured through the
[JavaHprofConfig](/docs/reference/trace-config-proto.autogen#JavaHprofConfig)
section of the trace config.

```protobuf
data_sources {
  config {
    name: "android.java_hprof"
    java_hprof_config {
      process_cmdline: "com.google.android.inputmethod.latin"
      dump_smaps: true
    }
  }
}
```
