It's All Relative

Performance Minded

16 Mar 2016

7 mins read

CompileCommand JVM option

CompileCommand is another JVM option that you can play with in conjunction (or not) with the famous PrintAssembly. But there is more…

In this article I will give you a description of all useful commands available with this option.

help

The first command you can try is the following:

java -XX:CompileCommand=help -version
  CompileCommand and the CompilerOracle allows simple control over
  what's allowed to be compiled.  The standard supported directives
  are exclude and compileonly.  The exclude directive stops a method
  from being compiled and compileonly excludes all methods except for
  the ones mentioned by compileonly directives.  The basic form of
  all commands is a command name followed by the name of the method
  in one of two forms: the standard class file format as in
  class/name.methodName or the PrintCompilation format
  class.name::methodName.  The method name can optionally be followed
  by a space then the signature of the method in the class file
  format.  Otherwise the directive applies to all methods with the
  same name and class regardless of signature.  Leading and trailing
  *'s in the class and/or method name allows a small amount of
  wildcarding.

  Examples:

  exclude java/lang/StringBuffer.append
  compileonly java/lang/StringBuffer.toString ()Ljava/lang/String;
  exclude java/lang/String*.*
  exclude *.toString
java version "1.8.0_51"
Java(TM) SE Runtime Environment (build 1.8.0_51-b16)
Java HotSpot(TM) 64-Bit Server VM (build 25.51-b03, mixed mode)

This is a start. But this online help is in fact quite incomplete. There is other commands/directives than those mentioned above. To find out the others, we have to look at the documentation page here which is complete in this case.

dontinline

This command is useful if you want to control some behavior related to inlining. For example, JMH is using this directive through the CompilerControl annotation. Let’s take an example:

    public static void main(String[] args) throws InterruptedException {
        List<String> list = new ArrayList<>();
        for (int i = 0; i < 10_000; i++)
        {
            list.add(String.valueOf(i));
        }
        Thread.sleep(1000);
    }

If we execute this code with the following JVM options:

-XX:UnlockDiagnosticVMOptions -XX:+PrintCompilation -XX:+PrintInlining
233   65    java.util.ArrayList::add (29 bytes)
              @ 7   java.util.ArrayList::ensureCapacityInternal (23 bytes)   inline (hot)
                @ 19   java.util.ArrayList::ensureExplicitCapacity (26 bytes)   inline (hot)
                  @ 22   java.util.ArrayList::grow (45 bytes)   too big

If we add this JVM option:

-XX:CompileCommand="dontinline java.util.ArrayList::ensureCapacityInternal"

we will get:

[...]
290  65 java.util.ArrayList::add (29 bytes)
         @ 7 java.util.ArrayList::ensureCapacityInternal (23 bytes) disallowed by CompilerOracle
[...]

No more inlining because disallowed by CompilerOracle where CompilerOracle is the CompileCommand JVM option.

inline

Same here with this command which try to force inlining on a specific method. If we execute the previous code with the following JVM options:

-XX:UnlockDiagnosticVMOptions -XX:+PrintCompilation -XX:+PrintInlining
612   64      java.util.ArrayList::ensureExplicitCapacity (26 bytes)
                @ 22   java.util.ArrayList::grow (45 bytes)   too big
                @ 19   java.util.ArrayList::ensureExplicitCapacity (26 bytes)   inline (hot)
                  @ 22   java.util.ArrayList::grow (45 bytes)   too big

We can see that grow method is not inlined in this context because considered as too big (above 35 byte codes). But if we add the CompileCommand:

-XX:CompileCommand="inline java.util.ArrayList::grow"

The result is the following:

361   64     java.util.ArrayList::ensureExplicitCapacity (26 bytes)
                @ 22   java.util.ArrayList::grow (45 bytes)   force inline by CompilerOracle
                  @ 28   java.util.ArrayList::hugeCapacity (26 bytes)   never executed
                  @ 38   java.util.Arrays::copyOf (13 bytes)   executed < MinInliningThreshold times
                @ 19   java.util.ArrayList::ensureExplicitCapacity (26 bytes)   inline (hot)
                  @ 22   java.util.ArrayList::grow (45 bytes)   force inline by CompilerOracle
                    @ 28   java.util.ArrayList::hugeCapacity (26 bytes)   never executed
                    @ 38   java.util.Arrays::copyOf (13 bytes)   executed < MinInliningThreshold times

The reason for inlining the method is now clear: force inline by CompilerOracle

print

The print command is in fact similar to the PrintAssembly JVM option. But the added value is the ability to specify the exact or prefix name method that we want to disassemble.

-XX:CompileCommand="print java.util.ArrayList::add"
CompilerOracle: print java/util/ArrayList.add
Compiled method (c2)     176   65             java.util.ArrayList::add (29 bytes)
 total in heap  [0x0000000002558a10,0x0000000002558fb0] = 1440
 relocation     [0x0000000002558b30,0x0000000002558b60] = 48
 main code      [0x0000000002558b60,0x0000000002558ca0] = 320
 stub code      [0x0000000002558ca0,0x0000000002558cc8] = 40
 oops           [0x0000000002558cc8,0x0000000002558cd0] = 8
 metadata       [0x0000000002558cd0,0x0000000002558cf0] = 32
 scopes data    [0x0000000002558cf0,0x0000000002558dc0] = 208
 scopes pcs     [0x0000000002558dc0,0x0000000002558f80] = 448
 dependencies   [0x0000000002558f80,0x0000000002558f88] = 8
 handler table  [0x0000000002558f88,0x0000000002558fa0] = 24
 nul chk table  [0x0000000002558fa0,0x0000000002558fb0] = 16
Loaded disassembler from C:\Program Files\Java\jdk1.8.0_40\jre\bin\server\hsdis-amd64.dll
Decoding compiled method 0x0000000002558a10:
Code:
[Disassembling for mach='i386:x86-64']
[Entry Point]
[Constants]
  # {method} {0x000000000a4840f8} 'add' '(Ljava/lang/Object;)Z' in 'java/util/ArrayList'
  # this:     rdx:rdx   = 'java/util/ArrayList'
  # parm0:    r8:r8     = 'java/lang/Object'
  #           [sp+0x40]  (sp of caller)

[...]

[Stub Code]
  0x0000000002558ca0: movabs rbx,0x0            ;   {no_reloc}
  0x0000000002558caa: jmp    0x0000000002558caa  ;   {runtime_call}
[Exception Handler]
  0x0000000002558caf: jmp    0x000000000253c9e0  ;   {runtime_call}
[Deopt Handler Code]
  0x0000000002558cb4: call   0x0000000002558cb9
  0x0000000002558cb9: sub    QWORD PTR [rsp],0x5
  0x0000000002558cbe: jmp    0x0000000002517600  ;   {runtime_call}
  0x0000000002558cc3: hlt    
  0x0000000002558cc4: hlt    
  0x0000000002558cc5: hlt    
  0x0000000002558cc6: hlt    
  0x0000000002558cc7: hlt    
OopMapSet contains 6 OopMaps

#0 
OopMap{rbp=Oop [8]=Oop off=180}
#1 
OopMap{[8]=Oop off=216}
#2 
OopMap{rbp=NarrowOop [8]=Oop off=236}
#3 
OopMap{rbp=NarrowOop [8]=Oop off=256}
#4 
OopMap{rbp=Oop [0]=Oop [20]=NarrowOop off=288}
#5 
OopMap{off=300}
Java HotSpot(TM) 64-Bit Server VM warning: printing of assembly code is enabled; turning on DebugNonSafepoints to gain additional output

Wildcards are also allowed like mentioned in the online help:

-XX:CompileCommand="print java.util.ArrayList::*"

exclude

By default, all methods can be compiled if the compile threshold is reached. However we can exclude one or set of methods with the exclude command. Let’s try with an example:

-XX:+PrintCompilation -XX:CompileCommand="exclude java.util.ArrayList::add"

The output will be:

[...]
    237   61             java.lang.Integer::getChars (131 bytes)
    249   62             java.lang.String::<init> (10 bytes)
    250   63             java.util.ArrayList::ensureCapacityInternal (23 bytes)
    250   64             java.util.ArrayList::ensureExplicitCapacity (26 bytes)
### Excluding compile: java.util.ArrayList::add
made not compilable on all levels  java.util.ArrayList::add (29 bytes)   excluded by CompilerOracle
    251   65             java.lang.String::valueOf (5 bytes)
    251   66             java.lang.Integer::toString (48 bytes)

compileonly

On the opposite, compileonly command allow to compile only the method specified in argument. Example:

-XX:+PrintCompilation -XX:CompileCommand="compileonly java.util.ArrayList::add"

The output will be:

[...]
    196   43     n       java.lang.invoke.MethodHandle::invokeBasic(ILL)L (native)   
    196   44     n       java.lang.invoke.MethodHandle::linkToSpecial(LILLL)L (native)   (static)
    227   45             java.util.ArrayList::add (29 bytes)

Native methods are printed but no special operation is performed as JIT has nothing to do with them.

log

By default when you are using LogCompilation to output all compilation information from JIT, all methods compiled are included in this log. If we want to only output a specified method, we can use the log command in the same way than previous one. Example:

-XX:+UnlockDiagnosticVMOptions -XX:+LogCompilation -XX:CompileCommand="log java.util.ArrayList::add"

The output file can be opened with JITWatch.

option

You can use this command to enable a JVM option only for the specified method or set of methods. For example let’s print the inlining tree for the ArrayList.add method:

-XX:CompileCommand="option java.util.ArrayList::add,PrintInlining"

The output will be:

CompilerOracle: option java/util/ArrayList.add bool PrintInlining = true
            @ 7   java.util.ArrayList::ensureCapacityInternal (23 bytes)   inline (hot)
              @ 19   java.util.ArrayList::ensureExplicitCapacity (26 bytes)   inline (hot)
                @ 22   java.util.ArrayList::grow (45 bytes)   too big

Very handy to avoid to be flooded by information.

CompileCommandFile

The previous commands can be combine into a file for convenience. By default the file is read from current directory with the name .hotspot_compiler. But you can specify your own file name with:

-XX:CompileCommandFile=MyCompilerFile.txt

This is an example of content:

compileonly java.util.ArrayList::add
compileonly java.util.ArrayList::ensureCapacityInternal
compileonly java.util.ArrayList::ensureExplicitCapacity
compileonly java.util.ArrayList::grow
inline java.util.ArrayList::grow
option java.util.ArrayList::add,PrintInlining

And here is the output:

CompilerOracle: compileonly java/util/ArrayList.add
CompilerOracle: compileonly java/util/ArrayList.ensureCapacityInternal
CompilerOracle: compileonly java/util/ArrayList.ensureExplicitCapacity
CompilerOracle: compileonly java/util/ArrayList.grow
CompilerOracle: inline java/util/ArrayList.grow
CompilerOracle: option java/util/ArrayList.add bool PrintInlining = true
          @ 7   java.util.ArrayList::ensureCapacityInternal (23 bytes)   inline (hot)
            @ 19   java.util.ArrayList::ensureExplicitCapacity (26 bytes)   inline (hot)
              @ 22   java.util.ArrayList::grow (45 bytes)   force inline by CompilerOracle
                @ 28   java.util.ArrayList::hugeCapacity (26 bytes)   failed initial checks
                @ 38   java.util.Arrays::copyOf (13 bytes)   failed initial checks

Update: In JDK 9 there will be a new mechanism: Compiler Control. Thanks Mark Price for pointing me this JEP.

References