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