RFR: JDK-8317683: Add JIT memory statistics

Thomas Stuefe stuefe at openjdk.org
Tue Oct 10 16:36:40 UTC 2023


This proposal adds a way to monitor C1/C2 memory usage per compilation. 

We are flying a bit blind there: NMT is okay for initial triaging but no help when digging deeper into compiler footprint. NMT shows how much native memory the JIT uses but lumps together footprints from concurrent compilations, making its peak values arbitrary. And we have no way to associate the NMT numbers with individual allocations.

The statistic added by this patch shows footprint *per compilation*. It measures memory spikes per thread for a single compilation. It then presents them with additional information, e.g., the node count (for C2). 

Example printout:


112703:
Compilation memory statistics

Legend:
  total  : memory allocated via arenas while compiling
  NA     : ...how much in node arenas (if c2)
  RA     : ...how much in resource areas
  #nodes : ...how many nodes (if c2)
  time   : time of last compilation (sec)
  type   : compiler type
  #rc    : how often recompiled
  thread : compiler thread

total     NA        RA        #nodes  time    type  #rc thread              method
30273336  6383040   18027000  18257   1,979   c2    2   0x00007f6f0c136460  java/lang/ClassLoader.loadClass((Ljava/lang/String;)Ljava/lang/Class;) 
27690456  5892160   16211800  15179   3,643   c2    2   0x00007f6f0c0433e0  org/springframework/cache/interceptor/CacheOperationSourcePointcut.matches((Ljava/lang/reflect/Method;Ljava/lang/Class;)Z) 
...
...


[full example printout here](https://github.com/openjdk/jdk/files/12853477/compiler-statistic-petclinic.txt)

#### Usage

The statistic needs to be enabled with a new diagnostic switch (`-XX:+CompilationMemStat`) and then can be printed with a new jcmd:

`jcmd xxx Compiler.memory`

You can specify a cutoff memory threshold to filter only the most expensive compilations, and switch between human-readable format:

`jcmd xxx Compiler.memory -s=10m -H`

The statistic can also be printed at the end of VM life (similar to metaspace- or NMT-statistics):

`-XX:+PrintCompilationMemStatAtExit`

#### Technical details

The patch exploits the fact that compilers use arenas almost exclusively. It hooks into `Arena::grow()` to track allocations. That makes memory tracking very cheap at an acceptable loss in fidelity (we only track arena chunk allocations, not individual Arena allocations).

Peak values are tracked per compiler thread. That works since all arenas used during compilation are associated with a compiler thread and never change that association during the compilation.

We measure total footprint, and when we peaked, how much of that footprint is caused by node arenas and resource areas (since these tend to be the most significant contributors). In addition, we measure live node count at peak time.

The statistic backend uses a ResourceHash map. It keeps statistics per compiled method. Its entries are eternal, and the association is by name: the hashmap key is the (classname-methodname-signature) triple. So these entries have no connection to `Method*` and survive class unloading. Hashmap key uses existing `Symbol*`s for name identity and to save memory.

The hashmap entries get updated when the method with the same name is recompiled. If different loaders load the same class, they share an entry. I did not want to make this any more complicated by separating loaders.

#### Costs:

88 bytes per compiled method, plus ~64K for the hashtable.

Runtime costs are very small. The costly part is storing the statistics after a compilation finishes. That involves updating a global hash table under lock protection. However, I did not see measurable timing regressions. 

Nevertheless, this diagnostic feature is, by default, off.

#### Tests:

I tested this manually with class unloading and a lot of compiler churn. I cross-checked the numbers against RSS footprint increase and against NMT numbers.

Patch comes with own basic jtreg regression tests.

As a test (not in the final version), I enabled the statistic unconditionally and let GHA tests run through, all tests were green.

-------------

Commit messages:
 - Merge branch 'openjdk:master' into arena-per-thread-stats-for-compiler
 - Compilation memory statistic

Changes: https://git.openjdk.org/jdk/pull/16076/files
 Webrev: https://webrevs.openjdk.org/?repo=jdk&pr=16076&range=00
  Issue: https://bugs.openjdk.org/browse/JDK-8317683
  Stats: 764 lines in 25 files changed: 739 ins; 2 del; 23 mod
  Patch: https://git.openjdk.org/jdk/pull/16076.diff
  Fetch: git fetch https://git.openjdk.org/jdk.git pull/16076/head:pull/16076

PR: https://git.openjdk.org/jdk/pull/16076


More information about the hotspot-compiler-dev mailing list