Friday, June 20, 2014

CodeCache is full. Compiler has been disabled.

You usually see this error when compiling complex projects



Java HotSpot(TM) 64-Bit Server VM warning: CodeCache is full. Compiler has been disabled.
Java HotSpot(TM) 64-Bit Server VM warning: Try increasing the code cache size using -XX:ReservedCodeCacheSize=
Code Cache  [0x0000000111985000, 0x0000000114925000, 0x0000000114985000)
 total_blobs=15925 nmethods=15527 adapters=344 free_code_cache=814Kb largest_free_block=431360 

Although it is merely a warning, "Compiler has been disabled" sounds ominous. Fortunately this warning does not affect the compilation process; the resulting bytecode is exactly the same if produced without this warning.

Unfortunately it does affect performance. Compiling sbt 0.13.5 on an Intel i7-3615QM processor with 16GB RAM takes 486 seconds.

Why is the CodeCahe Full?

I will not delve further into the technical details, because my knowledge about JVM internals and compilers is lacking. Pete de Zwart's excellent article explains how code cache works and the impact on compilation.

The code cache contains compiled native code to speed to the compilation process; without which the compilation process will take much longer.

The default Code Cache value for 64-bit JVM 7 and above is 48MB. Prior to that, it was 1GB which caused unnecessary RAM to be reserved. 48MB is determined to offer the best balance. If you are compiling a complicated project, the code cache may very well be full.

The initial stages of compilation went well, all 8 CPU cores were almost fully utilized. The compilation process became very slow when it encountered these code snippets


[warn] /Users/hanxue/Github/sbt/util/collection/src/main/scala/sbt/INode.scala:27: abstract type T in type pattern EvaluateSettings.this.init.Keyed[s,T] is unchecked since it is eliminated by erasure
[warn]         case k: Keyed[s, T] => single(getStatic(k.scopedKey), k.transform)
[warn]                 ^
[warn] /Users/hanxue/Github/sbt/util/collection/src/main/scala/sbt/INode.scala:28: abstract type T in type pattern EvaluateSettings.this.init.Apply[k,T] is unchecked since it is eliminated by erasure
[warn]         case a: Apply[k,T] => new MixedNode[k,T]( a.alist.transform[Initialize, INode](a.inputs, transform), a.f, a.alist)
[warn]                 ^
[warn] /Users/hanxue/Github/sbt/util/collection/src/main/scala/sbt/INode.scala:29: abstract type T in type pattern EvaluateSettings.this.init.Bind[s,T] is unchecked since it is eliminated by erasure
[warn]         case b: Bind[s,T] => new BindNode[s,T]( transform(b.in), x => transform(b.f(x)))
[warn]                 ^


During this time, CPU usage fell down to 1-2 cores.

Disable Code Cache Flushing

You can disable code cache flushing totally by passing the JVM option XX:-UseCodeCacheFlushing

I believe this has the equivalent effect in an sbt configuration file:


scala javaOptions in (Test,run) += -XXUseCodeCacheFlushing


You will not see the warning messages, but then again you are not taking advantage of an important JVM feature to speed up compilation.

Increase Code Cache Size

Instead of disabling it, why not increase the code cache size? In my sbt launcher script, I simply add -XX:ReservedCodeCacheSize=128M. This is how my sbt launcher script looks like:


#!/bin/sh
test -f ~/.sbtconfig && . ~/.sbtconfig
exec java -XX:ReservedCodeCacheSize=128M -Xms512M -Xmx1536M -Xss1M -XX:+CMSClassUnloadingEnabled ${SBT_OPTS} -jar /usr/local/Cellar/sbt/0.13.5/libexec/sbt-launch.jar "$@"

Sped Up Compilation

After increasing my code cache size, I no longer see the same code cache full warning messages. Compiling sbt 0.13.5 now takes only 236 seconds.

In short, a 105.9% speed up of the compilation process.



No comments:

Post a Comment