Memory

The JIT code cache: ReservedCodeCacheSize and 'CodeCache is full'

Memory · Published 2026-10-20

Macro photograph of a dark printed circuit board with silkscreen labels, pads and small surface-mount components
Compiled machine code is kept in its own region of native memory, the code cache. Photo: D Coetzee, Flickr, CC0.

HotSpot does not run hot Java methods from bytecode for long: the JIT compilers turn them into machine code and store it in a dedicated native memory area called the code cache. When that area fills up, the JVM stops compiling, and an application can slowly fall back to interpreted speed without any exception being thrown.

What lives in the code cache

The code cache holds the output of the JIT compilers (C1 and C2 in a standard HotSpot build), plus code the JVM generates for itself: the interpreter, stubs and adapters between interpreted and compiled code. With tiered compilation, which is the default, a method is often compiled more than once: first by C1 with profiling code, later by C2 with optimisations based on that profile. Both versions occupy space for a while.

The cache is reserved at startup as one contiguous range of virtual memory and committed as it grows. Its maximum size is fixed for the life of the process.

Default sizes

The java launcher reference gives the numbers for -XX:ReservedCodeCacheSize:

ConfigurationDefault maximum
Tiered compilation (default)240 MB
-XX:-TieredCompilation48 MB
Upper limit of the option2 GB (larger values are rejected)

The same reference documents -XX:InitialCodeCacheSize, whose default depends on the platform, and warns that the maximum must not be smaller than the initial size.

The segmented code cache

JEP 197, delivered in JDK 9, split the cache into separate code heaps. The launcher reference says that with -XX:+SegmentedCodeCache there are separate segments for non-method code, profiled methods and non-profiled methods, that these segments are not resized at runtime, and that the feature is on by default when tiered compilation is enabled and ReservedCodeCacheSize is at least 240 MB.

SegmentContentsSize option
non-nmethodsJVM internal code: interpreter, stubs, adapters-XX:NonNMethodCodeHeapSize
profiled nmethodsC1 code with profiling, short-lived-XX:ProfiledCodeHeapSize
non-profiled nmethodsFully optimised C2 code (and C1 code without profiling)-XX:NonProfiledCodeHeapSize

Because the segments do not borrow from each other, one segment can fill while the others still have room. Monitoring the total is therefore not enough; watch each segment.

Symptoms of a full code cache

When there is no space left for new compiled code, HotSpot prints a warning on the console or in the JVM output, typically:

OpenJDK 64-Bit Server VM warning: CodeCache is full. Compiler has been disabled.
OpenJDK 64-Bit Server VM warning: Try increasing the code cache size using -XX:ReservedCodeCacheSize=

Nothing is thrown to application code. What you see instead is indirect:

Since the warning is the only explicit signal, make sure the JVM's own output is captured by your log pipeline.

Monitoring

jcmd

$ jcmd <pid> Compiler.codecache

This prints each code heap with its size, used space, the high-water mark (max_used) and free space, followed by counts of compiled methods and whether compilation is still enabled. A max_used close to size in any segment is your early warning. jcmd <pid> Compiler.codelist lists the compiled methods themselves, and jcmd <pid> help shows which compiler commands your release supports.

Memory pool MXBeans

Each code heap is exposed as a MemoryPoolMXBean. With a segmented cache the pool names are CodeHeap 'non-nmethods', CodeHeap 'profiled nmethods' and CodeHeap 'non-profiled nmethods'; with a single segment (JDK 8, or segmentation disabled) there is one pool called Code Cache. Most metrics agents that export JVM memory pools pick these up automatically.

for (MemoryPoolMXBean p : ManagementFactory.getMemoryPoolMXBeans()) {
    if (p.getName().startsWith("CodeHeap") || p.getName().equals("Code Cache")) {
        MemoryUsage u = p.getUsage();
        System.out.printf("%s used=%d max=%d%n", p.getName(), u.getUsed(), u.getMax());
    }
}

At exit and in Flight Recorder

-XX:+PrintCodeCache prints a summary when the JVM exits, which is handy in load tests. JDK Flight Recorder records code cache statistics and a jdk.CodeCacheFull event; see the JFR guide.

When to raise ReservedCodeCacheSize

Raise it when the measurements show a segment near its limit in steady state, or when you see the warning. Typical cases are very large applications: many frameworks, generated code, scripting engines, or many dynamically generated classes that are compiled. A doubling is a reasonable first step; measure again under realistic traffic.

# a large application server: give the JIT more room
$ java -XX:ReservedCodeCacheSize=512m -jar app.jar

Points to keep in mind:

Code cache flushing

When space runs short, HotSpot can evict compiled methods to make room before giving up on compilation. The launcher reference describes -XX:+UseCodeCacheFlushing, enabled by default, as flushing the code cache before shutting down the compiler. Flushing helps a cache that is full of cold code; it does not help an application whose hot code genuinely does not fit. If your graphs show the cache filling, being flushed, refilling and filling again, with compiler threads busy long after startup, the cache is too small.

Native Memory Tracking view

With -XX:NativeMemoryTracking=summary, the Code category of jcmd <pid> VM.native_memory summary shows reserved and committed code cache memory alongside every other JVM subsystem. It is the quickest way to see the code cache in the context of total process size. See the NMT guide.

Checklist

Sources