Search code examples
javaperformancejvmperformance-testingjvm-hotspot

Is it possible to disable `-XX:+PrintCompilation` and `-verbose:gc"` at runtime?


As suggested in this answer, I am using the following flags when running performance tests.

  -XX:+PrintCompilation
  -verbose:gc

This was very useful for debugging what JVM activities were happening during the timing phase, but the output is less useful when I am merely computing statistics and printing output about the benchmark that just ran.

Is there a way to disable one or both of these flags at runtime so that I can turn them off after the timing phase?


Solution

  • It's easy to turn off GC logs in runtime, since GC logs are included in Unified JVM Logging framework.

    From the command line

    jcmd <pid> VM.log what=gc=off
    

    From within the application

    ManagementFactory.getPlatformMBeanServer().invoke(
            new ObjectName("com.sun.management:type=DiagnosticCommand"),
            "vmLog",
            new Object[]{new String[]{"what=gc=off"}},
            new String[]{"[Ljava.lang.String;"}
    );
    

    Unfortunately, -XX:+PrintCompilation is not a manageable flag and does not obey Unified JVM Logging. However, it's possible to change it, too.

    I already showed in this answer how to modify a JVM flag externally using the Serviceability Agent. Here is one more way to do this.

    The idea is to find the memory address of the flag and modify the value right in the memory. Below is an example how to achieve this on Linux.

    1. Find the base address of the loaded JVM library:
    $ grep libjvm.so /proc/<pid>/maps
    7f57435ca000-7f574470d000 r-xp 00000000 fd:00 2342674    /usr/java/jdk-11/lib/server/libjvm.so
    ^^^^^^^^^^^^
    
    1. Find the offset of PrintCompilation symbol in libjvm.so:
    $ nm /usr/java/jdk-11/lib/server/libjvm.so | grep PrintCompilation
    000000000144d7ff b PrintCompilation
    ^^^^^^^^^^^^^^^^
    
    1. Now write 0 into the process memory at the address base + offset:
    $ printf '\x00' | dd of=/proc/<pid>/mem bs=1 count=1 seek=$((0x7f57435ca000 + 0x144d7ff)) conv=notrunc
    

    That's it. PrintCompilation flag has been turned off.

    Bonus

    The same trick can be done directly from a Java application: read /proc/pid/maps like a regular file, parse ELF format of libjvm.so to find the symbol offset, and finally use Unsafe to write a byte at the given address. Here is the complete example.

    Update

    I've added a macOS example of modifying JVM flags in runtime from within a Java application. The usage is as simple as

    VMFlags.setBooleanFlag("PrintCompilation", true);