RFR: 8326205: Grouping frequently called C2 nmethods in CodeCache

Chad Rakoczy duke at openjdk.org
Wed Dec 10 21:19:45 UTC 2025


On Fri, 17 Oct 2025 18:10:16 GMT, Vladimir Kozlov <kvn at openjdk.org> wrote:

>> ### Summary
>> This PR implements [JDK-8326205](https://bugs.openjdk.org/browse/JDK-8326205), introducing experimental support for grouping hot code within the CodeCache.
>> 
>> ### Description
>> The feature works by periodically sampling the execution of C2-compiled methods to identify hot code, then relocating those methods into a dedicated `HotCodeHeap` section of the CodeCache.
>> 
>> Sampling is performed by the `HotCodeSampler`, which runs on a new dedicated `HotCodeGrouper` thread. The thread wakes up every `HotCodeIntervalSeconds` (default 300s) and collects samples for a duration of `HotCodeSampleSeconds` (default 120s). During each sampling period, it iterates over all Java threads, inspects their last Java frame, obtains the current program counter (PC), and maps it to the corresponding nmethod. This allows the sampler to maintain a profile of the most frequently executed methods.
>> 
>> The `HotCodeGrouper` uses the sampling data to select methods for grouping. Methods are ranked by sample count to form the candidate set. The grouper then relocates these methods (along with their callees, which has been shown to improve performance on AArch64 due to better branch prediction) into the `HotCodeHeap` in descending order of hotness, continuing until the fraction of samples attributable to hot methods exceeds `HotCodeSampleRatio` (default 0.8). The process continues to ensure that the hot-method ratio remains above the threshold.
>> 
>> The `HotCodeHeap` is a new code heap segment with a default size of 20% of the non-profiled heap, though this can be overridden. This size was chosen based on the principle that roughly 20% of methods contribute to 80% of the work. Only C2-compiled nmethods are eligible for relocation, and the relocation process leverages existing infrastructure from [JDK-8316694](https://bugs.openjdk.org/browse/JDK-8316694).
>> 
>> Relocation occurs entirely on the grouper thread and runs concurrently with the application. To maintain correctness, the thread acquires the `CodeCache_lock` and `Compile_lock` during relocation but releases these locks between individual relocations to avoid blocking GC safepoints. Removal of nmethods from the `HotCodeHeap` is handled by the GC.
>> 
>> ### Performance
>> Testing has shown up to a 20% latency reduction in an internal service with a large CodeCache (512 MB). Public benchmark results are forthcoming.
>> 
>> ### Testing
>> * CodeCache tests have been updated to cover the new `HotCodeHeap`.  
>> * Dedicated...
>
> src/hotspot/share/compiler/compilerDefinitions.cpp line 346:
> 
>> 344:       vm_exit_during_initialization("HotCodeHeap requires C2 enabled", NULL);
>> 345:     }
>> 346:   }
> 
> Put it under `#ifdef COMPILER2`

I moved most of the new code under `#ifdef COMPILER2` and into `opto/c2_globals.hpp`

I'm unsure what to do about `HotCodeHeapSize`. I can also make that flag C2 only which requires some updates to `codeCache.cpp` and overall I don't think it makes the code too messy.

The biggest problem with that is what to do about `CodeBlobType` with the addition of the `MethodHot` type

Before:


enum class CodeBlobType {
  MethodNonProfiled   = 0,    // Execution level 1 and 4 (non-profiled) nmethods (including native nmethods)
  MethodProfiled      = 1,    // Execution level 2 and 3 (profiled) nmethods
  NonNMethod          = 2,    // Non-nmethods like Buffers, Adapters and Runtime Stubs
  All                 = 3,    // All types (No code cache segmentation)
  NumTypes            = 4     // Number of CodeBlobTypes
};


After:


enum class CodeBlobType {
  MethodNonProfiled   = 0,    // Execution level 1 and 4 (non-profiled) nmethods (including native nmethods)
  MethodProfiled      = 1,    // Execution level 2 and 3 (profiled) nmethods
  MethodHot           = 2,    // Nmethods predicted to be always hot
  NonNMethod          = 3,    // Non-nmethods like Buffers, Adapters and Runtime Stubs
  All                 = 4,    // All types (No code cache segmentation)
  NumTypes            = 5     // Number of CodeBlobTypes
};


If we do

#ifdef COMPILER2
    /* AFTER */
#else
    /* BEFORE */
#endif

Things start getting weird if blob types have different values in different builds. Particuatlly in tests such as [BlobType.java](https://github.com/openjdk/jdk/blob/master/test/lib/jdk/test/whitebox/code/BlobType.java). I think that we need to leave `CodeBlobType::MethodHot` regardless if C2 is present or not.

In terms of `HotCodeHeapSize` I'm not sure the best place for it. I think it makes sense to leave with the other code heap flags which also allows us to print an error if specified without C2. On the other hand if it requires C2 is should be in `opto/c2_globals.hpp`

@vnkozlov @eastig What do you think?

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

PR Review Comment: https://git.openjdk.org/jdk/pull/27858#discussion_r2475158385


More information about the hotspot-runtime-dev mailing list