https://docs.oracle.com/javase/8/embedded/develop-apps-platforms/codecache.htm
The JVM generates native code for a variety of reasons, including:
for the dynamically generated interpreter loop
Java Native Interface (JNI) stubs
Java methods that are compiled into native code by the just-in-time (JIT) compiler. The JIT is by far the biggest user of the codecache.
How to Use the Codecache Options of the java Command#
The options listed in the following sections share the following characteristics.
All options are
--XXoptions, for example,-XX:InitialCodeCacheSize=32m. Options that have true/false values are specified using+for true and-for false. For example,-XX:+PrintCodeCachesets this option to true.For any option that has “varies” listed as the default value, run the launcher with
XX:+PrintFlagsFinalto see your platform’s default value.If the default value for an option differs depending on which JVM is being used (client or server), then both defaults are listed, separated by a ‘/’. The client JVM default is listed first. The minimal JVM uses the same JIT as the client JVM, and therefore has the same defaults.
Codecache Size Options#
Table 15-1 summarizes the codecache size options. See also Constraining the Codecache Size.
Table 15-1 Codecache Size Options
Option |
Default |
Description |
|---|---|---|
|
160K (varies) |
Initial code cache size (in bytes) |
|
32M/48M |
Reserved code cache size (in bytes) - maximum code cache size |
|
32K/64K |
Code cache expansion size (in bytes) |
Codecache Flush Options#
Table 15-2 summarizes the codecache flush options.
Table 15-2 Codecache Flush Options
Option |
Default |
Description |
|---|---|---|
|
false |
Exit the JVM if the codecache fills |
|
false |
Attempt to sweep the codecache before shutting off compiler |
|
30 |
Minimum number of seconds between codecache sweeping sessions |
|
500K |
When less than the specified amount of space remains, stop compiling. This space is reserved for code that is not compiled methods, for example, native adapters. |
Compilation Policy Options#
Table 15-3 summarizes the compilation policy (when to compile) options.
Table 15-3 Compilation Policy Options
Option |
Default |
Description |
|---|---|---|
|
1000 or 1500/10000 |
Number of interpreted method invocations before (re-)compiling |
|
140 to 933 |
NON_TIERED number of method invocations/branches (expressed as a percentage of |
Compilation Limit Options#
Table 15-4 summarizes the compilation limit options, which determine how much code is compiled).
Table 15-4 Compilation Limit Options
Option |
Default |
Description |
|---|---|---|
|
9 |
Maximum number of nested calls that are inlined |
|
35 |
Maximum bytecode size of a method to be inlined |
|
250 |
Minimum invocation count a method needs to have to be inlined |
|
true |
Inline synchronized methods |
Diagnostic Options#
Table 15-5 summarizes the diagnostic options.
Table 15-5 Diagnostic Options
Option |
Default |
Description |
|---|---|---|
|
false |
Print all JVM options after argument and ergonomic processing |
|
false |
Print the code cache memory usage when exiting |
|
false |
Print the code cache memory usage each time a method is compiled |
Constraining the Codecache Size#
Constraining the codecache size means the codecache is limited to a size that is less than what would an unconstrained codecache would use. The ReservedCodeCacheSize option determines the maximum size of the codecache. It defaults to a minimum of 32MB for the client JVM and 48MB for the server VM. For most Java applications, this size is so large that the application will never fill the entire codecache. Thus the codecache is viewed as being unconstrained, meaning the JIT will continue to compile any code that it thinks should be compiled.
When is Constraining the Codecache Size Useful?#
Applications that make state changes that result in a new set of methods being “hot” can benefit greatly from a constrained codecache.
A common state change is from startup to regular execution. The application might trigger a lot of compilation during startup, but very little of this compiled code is needed after startup. By constraining the codecache, you will trigger codecache flushing to throw away the code compiled during startup to make room for the code needed during application execution.
How to Constrain the Codecache Size#
The UseCodeCacheFlushing option turns codecache flushing on and off. By default it is on. You can disable this feature by specifying XX:-UseCodeCacheFlushing. When enabled, the codecache flushing is triggered when the memory available in the codecache is low. It is critical to enable codecache flushing if you constrain the codecache. If flushing is disabled, the JIT does not compile methods after the codecache fills up.
Reducing Compilations#
Two main command line options that affect how aggressively methods are compiled: CompileThreshold and OnStackReplacePercentage. CompileThreshold relates to the number of method invocations needed before the method is compiled. OnStackReplacePercentage relates to the number of backwards branches taken in a method before it gets compiled, and is specified as a percentage of CompileThreshold.
When a method’s combined number of backwards branches and invocations reaches or exceeds CompileThreshold * OnStackReplacePercentage / 100, the method is compiled. Note that there is also an option called BackEdgeThreshold, but it currently does nothing. Use OnStackReplacePercentage instead.
CodeCache size#
https://julio-falbo.medium.com/understand-jvm-and-jit-compiler-part-4-9738194ad06e
As always, the JVM will provide us a message in the console of our application telling us that the CodeCache is full. The message is:
VM warning: CodeCache is full. The compiler has been disabled.
We can easily see information about our Code Cache enabling a JVM option called: -XX:+PrintCodeCache.We can easily see information about our Code Cache enabling a JVM option called: -XX:+PrintCodeCache.
if you are running with Java8-, the output will be similar to this: (I will explain this difference later in this same article.)
CodeCache: size=245760Kb used=1149Kb max_used=1162Kb free=244610Kb
bounds [0x000000011489d000, 0x0000000114b0d000, 0x000000012389d000]
total_blobs=298 nmethods=40 adapters=172
compilation: enabled
If you are using Java 8 or above then the max size will be 240 megabytes but if you disable tiered compilation with the option -XX:-TieredCompilation, then the default size is 48 megabytes.
CodeCache Java8 vs Java9+#
Basically, in Java 9 we had the JEP (JDK Enhancement Proposal) 197 that was responsible to split the Code Cache into 3 different segments (also called Code Heaps), “non-method”, “profiled” and “non-profiled”.
The “non-method” code heap contains non-method code such as compiler buffers and bytecode interpreters. This code type stays in the code cache forever. The code heap has a fixed size of 3 MB and the remaining code cache is distributed evenly among the profiled and non-profiled code heaps.
The “profiled” code heap contains lightly optimized, profiled methods with a short lifetime.
And the “non-profiled” code heap contains fully optimized, non-profiled methods with a potentially long lifetime.
but why did they do that?
According to the official page of the JEP 197, the goals are:
Separate non-method, profiled, and non-profiled code
Shorter sweep times due to specialized iterators that skip non-method code
Improve execution time for some compilation-intensive benchmarks
Better control of JVM memory footprint
Decrease fragmentation of highly-optimized code
Improve code locality because code of the same type is likely to be accessed close in time
Better iTLB and iCache behavior
Establish a base for future extensions
Improved management of heterogeneous code; for example, Sumatra (GPU code) and AOT compiled code
Possibility of fine-grained locking per code heap
Future separation of code and metadata (see JDK-7072317)