JDK-8329433 : Reduce nmethod header size
  • Type: Enhancement
  • Component: hotspot
  • Sub-Component: compiler
  • Affected Version: 23
  • Priority: P4
  • Status: Resolved
  • Resolution: Fixed
  • OS: generic
  • CPU: generic
  • Submitted: 2024-04-02
  • Updated: 2024-09-24
  • Resolved: 2024-04-19
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
JDK 23
23 b20Fixed
Related Reports
Relates :  
Relates :  
Relates :  
Relates :  
Description
Depending on application nmethod's header may take up to 10% (or more) of total size.
Here the data I got on linux-86 running javac which compiles 10000 HelloWorld classes (we use this test in Leyden development):

$ /jdk/build/optimized/images/jdk/bin/java -XX:+PrintNMethodStatistics -XX:+PrintCodeCache -cp ./JavacBenchIter.jar JavacBenchIter 10000
args[0]: 10000
Statistics for 146 native nmethods:
 N. total size  = 75536
 N. relocation  = 2600
 N. main code   = 24935
Statistics for 2822 bytecoded nmethods for C1:
 total in heap  = 9463504 (100%)
 header         = 857888 (9.065226%)
 relocation     = 561344 (5.931672%)
 constants      = 352 (0.003720%)
 main code      = 5720240 (60.445263%)
 stub code      = 300488 (3.175230%)
 oops           = 22168 (0.234247%)
 metadata       = 132856 (1.403877%)
 scopes data    = 736232 (7.779698%)
 scopes pcs     = 858976 (9.076722%)
 dependencies   = 51928 (0.548719%)
 handler table  = 69192 (0.731146%)
 nul chk table  = 117248 (1.238949%)
 size > 32k     = 29
 max size       = 199920
Statistics for 1282 bytecoded nmethods for C2:
 total in heap  = 5560352 (100%)
 header         = 389728 (7.009053%)
 relocation     = 432288 (7.774472%)
 constants      = 800 (0.014388%)
 main code      = 2961984 (53.269722%)
 stub code      = 57240 (1.029431%)
 oops           = 24920 (0.448173%)
 metadata       = 165392 (2.974488%)
 scopes data    = 843816 (15.175586%)
 scopes pcs     = 437904 (7.875473%)
 dependencies   = 54872 (0.986844%)
 handler table  = 113424 (2.039871%)
 nul chk table  = 62336 (1.121080%)
 size > 32k     = 25
 max size       = 66744
Debug Data Chunks: 408613, shared 234476+0, non-SP's elided 0
PcDesc Statistics:  8169 queries, 6.70 comparisons per query
  caches=4104 queries=8169/0, hits=0+1299, tests=26366+28386, adds=6870
Dependency check (find_witness) calls=23229, steps=158704 (avg=6.8), singles=506
CodeHeap 'non-profiled nmethods': size=118592Kb used=5796Kb max_used=5796Kb free=112795Kb
 bounds [0x00007ff430524000, 0x00007ff430ad4000, 0x00007ff4378f4000]
CodeHeap 'profiled nmethods': size=118588Kb used=9320Kb max_used=9320Kb free=109267Kb
 bounds [0x00007ff4288f4000, 0x00007ff429214000, 0x00007ff42fcc3000]
CodeHeap 'non-nmethods': size=8580Kb used=1397Kb max_used=4322Kb free=7182Kb
 bounds [0x00007ff42fcc3000, 0x00007ff430103000, 0x00007ff430524000]
CodeCache: size=245760Kb, used=16513Kb, max_used=19438Kb, free=229244Kb
 total_blobs=4731, nmethods=4250, adapters=388, full_count=0
Compilation: enabled, stopped_count=0, restarted_count=0

Comments
Changeset: b704e912 Author: Vladimir Kozlov <kvn@openjdk.org> Date: 2024-04-19 16:11:17 +0000 URL: https://git.openjdk.org/jdk/commit/b704e91241b0f84d866f50a8f2c6af240087cb29
19-04-2024

A pull request was submitted for review. URL: https://git.openjdk.org/jdk/pull/18768 Date: 2024-04-12 22:43:15 +0000
12-04-2024

Latest fixes increased header size in optimized VM (-16 in product): CodeBlob header size: 72 nmethod header size: 256
12-04-2024

SA tests failed when run with -Xcomp.
11-04-2024

Current version may be "not go" because I see regression in serviceability tests ("index out of bounds") and 15% performance regression in SPECjvm2008-LU.small-ParGC on linux-x64. May be I need to scale down my changes to be safe.
11-04-2024

CTW tests failed because relocation size is > 64Kb and does not fit into uint16_t field.
10-04-2024

My prototype reduced header size from 304 to 248 in optimized 64-bit VM (and to 232 in product VM): jdk_git/build/optimized/images/jdk/bin/java -XX:+PrintNMethodStatistics -XX:+PrintCodeCache -Xmx256m -cp ./JavacBenchIter.jar JavacBenchIter 10000 args[0]: 10000 Statistics for 160 native nmethods: N. total size = 80808 N. relocation = 3344 N. main code = 34041 Statistics for 2822 bytecoded nmethods for C1: total in heap = 9233696 (100%) header = 699856 (7.579370%) relocation = 562096 (6.087443%) constants = 352 (0.003812%) main code = 5642824 (61.111214%) stub code = 301000 (3.259800%) oops = 22312 (0.241637%) metadata = 132968 (1.440030%) scopes data = 737888 (7.991253%) scopes pcs = 860464 (9.318739%) dependencies = 51968 (0.562808%) handler table = 69720 (0.755061%) nul chk table = 117640 (1.274029%) size > 32k = 29 max size = 198480 Statistics for 1312 bytecoded nmethods for C2: total in heap = 5821656 (100%) header = 325376 (5.589063%) relocation = 448464 (7.703375%) constants = 800 (0.013742%) main code = 3137368 (53.891335%) stub code = 59096 (1.015106%) oops = 26736 (0.459251%) metadata = 173176 (2.974686%) scopes data = 941144 (16.166260%) scopes pcs = 454272 (7.803141%) dependencies = 57304 (0.984325%) handler table = 115872 (1.990361%) nul chk table = 66224 (1.137546%) size > 32k = 31 max size = 84008 Debug Data Chunks: 427450, shared 245084+0, non-SP's elided 0 PcDesc Statistics: 8338 queries, 6.69 comparisons per query caches=4134 queries=8338/0, hits=0+1371, tests=26950+28810, adds=6967 Dependency check (find_witness) calls=23547, steps=161983 (avg=6.9), singles=505 CodeHeap 'non-profiled nmethods': size=118592Kb used=6070Kb max_used=6070Kb free=112521Kb bounds [0x00007f028c521000, 0x00007f028cb11000, 0x00007f02938f1000] CodeHeap 'profiled nmethods': size=118588Kb used=9096Kb max_used=9096Kb free=109491Kb bounds [0x00007f02848f1000, 0x00007f02851e1000, 0x00007f028bcc0000] CodeHeap 'non-nmethods': size=8580Kb used=1395Kb max_used=4319Kb free=7184Kb bounds [0x00007f028bcc0000, 0x00007f028c100000, 0x00007f028c521000] CodeCache: size=245760Kb, used=16561Kb, max_used=19485Kb, free=229196Kb total_blobs=4775, nmethods=4294, adapters=388, full_count=0 Compilation: enabled, stopped_count=0, restarted_count=0
10-04-2024

There are additional changes we can do to reduce nmethod's header size. Currently some stubs are not generated in STUBS section but in main code section. As result we can't use unit16_t offset for them: UnwindHandler stub: https://github.com/openjdk/jdk/blob/master/src/hotspot/share/code/nmethod.cpp#L1424 Exception handling stub in gen_continuation_enter() https://github.com/openjdk/jdk/blob/master/src/hotspot/cpu/x86/sharedRuntime_x86_64.cpp#L1516 Also Graal does not generate stub section at all: https://github.com/openjdk/jdk/blob/master/src/hotspot/share/code/nmethod.cpp#L1392 An other issue with Graal/JVMCI is that we build VM with JVMCI enabled by default and as result some space is wasted when Graal is not used: https://github.com/openjdk/jdk/blob/master/src/hotspot/share/code/nmethod.hpp#L233 I am thinking about adding small JVMCINMethod subclass of `nmethod` to put all these differences there. But there are some places where we expect that `sizeof(nmethod)` and `nmethod*` works for all JIT compilers. So we need to be careful. All above issues could be addressed by following RFEs.
10-04-2024

Based on data in description nmethod's header size is 304 in optimized 64-bit VM. There are 2 fields in CodeBlob which are not present in product VM: #ifndef PRODUCT AsmRemarks _asm_remarks; DbgStrings _dbg_strings; #endif // not PRODUCT They should be pointer size each based on code. So header size should be 288 in product VM.
03-04-2024