Monday, June 25, 2012

On Stack Replacement in HotSpot JVM

In JVM, JIT Compiler is used to convert bytecode at runtime prior to executing it natively.  Since the JIT does not have time to compile every single method in an Java application, all code starts out initially running in the interpreter, and once it becomes hot enough it gets scheduled for compilation.

The HotSpot VM can perform special compiles called On Stack Replacement compiles, or OSRs.  These are used when Java code contains a long-running loop (see the example here) that started executing in the interpreter.

As described in [1], it is important to know about OSR if you want to benchmark Java programs and have put everything in a loop to be run in a method such as main.  You may run into some pitfalls caused by OSR on your benchmark results.  Without further ado, I'll refer you to read [1] for more details.

On Stack Replacement (OSR)[2]

Normally the way Java code ends up in compiled code is that when invoking a method the interpreter detects that there's compiled code for it, and it dispatches to that instead of staying in the interpreter.  This does not help long-running loops that started in the interpreter since they are not being invoked again.

When a long-running loop is detected at runtime, HotSpot VM requests a compile that starts its execution at the first bytecode of loop instead of starting at the first bytecode in the method.  The resulting generated code takes an interpreter frame as its input and uses that state to begin its execution.  In this way, long-running loops are able to take advantage of compiled code.  The act of the generated code taking an interpreter frame as input to be execution is called On Stack Replace.

How to Detect If OSR Happened?

The diagnostic options -XX:+LogCompilation can emit a structured XML log of compilation related activity during a run of the virtual machine.  Because this is a diagnostic option, to enable it, you need to specify  -XX:+UnlockDiagnosticVMOptions first.  For example, to generate compilation log file, you can specify:

  • -XX:+UnlockDiagnosticVMOptions -XX:+LogCompilation -XX:LogFile=/<path to log>/logs/<name of log>.log 
From the log file, you can search "osr" to see if any loop in your application has been compiled:

<task_queued compile_id='1' compile_kind='osr' 
  method='java/util/jar/JarFile hasClassPathAttribute ()Z' bytes='275' 
  count='223' backedge_count='57584' iicount='223' osr_bci='157' stamp='0.233' 
  comment='tiered' hot_count='57584'/>
<nmethod compile_id='1' compile_kind='osr' compiler='C2' level='4' 
  entry='0x00002aaaab4d4d40' size='1576' address='0x00002aaaab4d4bd0' 
  relocation_offset='288' insts_offset='368' stub_offset='1072' 
  scopes_data_offset='1120' scopes_pcs_offset='1352' dependencies_offset='1512' 
  nul_chk_table_offset='1520' oops_offset='1096'
  method='java/util/jar/JarFile hasClassPathAttribute ()Z' bytes='275' 
  count='241' backedge_count='62159' iicount='241' stamp='0.237'/>


Every compile is assigned a compile id by the system at the point is enqueued and that's recorded as the compile_id attribute. The above example shows an on stack replacement (OSR) compile where the code is going to be used to replace an already existing activation. It is tagged by the compile_kind attribute as:
  •  compile_kind='osr'
The 'method' attribute is a string version of the VM name of the method with spaces separating the class, method name signature.

The 'bytes' attribute is number of bytecodes in the method.

'count' is the invocation count as recorded by the method invocation counters. Note that these counters are mainly used for triggering compiles and are not guaranteed to be an accurate reflection of the number of times a method has actually executed. Multiple threads may be updated these counters and sometimes the VM will reset a counter to a lower value to delay retriggering of compiles.

'iicount' is the interpreter invocation count. This is a separate copy of the invocation count which is maintained by the profiling support. Again it's not guaranteed to be accurate since multiple threads may update it but it's never reset so it's reasonably accurate.

The 'backedge_count' attribute is used to detect methods that contain loops and to cause them to get compiled earlier than they would with just an invocation counter. Whenever this counter is incremented by the interpreter it checks it against a threshold, and if it crosses this threshold the interpreter requests a compile of that loop.

'stamp' gives a timestamp for the start time.  Many elements contain time stamps as the end which can be used to order them relative to events in other threads and the measure elapsed time. The time stamp is in seconds since the start of the VM and the start time of the VM is recorded in the hotspot_log element in the time_ms attribute.

References

  1. Robust Java benchmarking, Part 1: Issues 
  2. Java Performance by Charlie Hunt and Binu John
  3. LogCompilation Overview

Tuesday, June 5, 2012

What Are the Default HotSpot JVM Values?

Updated (09/16/2014):

In the latest JDK 8 releases, it only prints out product level options if you use, say, -XX:+PrintFlagsFinal.  To print other options, you could do something like this:
../bin/java -XX:+PrintFlagsFinal -XX:+UnlockExperimentalVMOptions  
  -XX:+UnlockDiagnosticVMOptions -version

Oftentimes you will find the needs to understand better the options provided by Oracle's (formerly Sun's) HotSpot Java Virtual Machine.  For example, when you try to tune the performance of Java applications, you definitively want to know what JVM parameter values are chosen by default.  From the defaults, you might start fine-tuning their values based on your application's characteristics.

There are two JVM options which can be useful to you:
  • -XX:+PrintFlagsInitial 
  • -XX:+PrintFlagsFinal
At JVM startup, the system will print the initial/final JVM values used.  The values provided by PrintFlagsInitial are values set by default and the values printed for PrintFlagsFinal are final values chosen after the dynamic runtime changes are made based on your hardware environment.

-XX:+PrintFlagsFinal


For example, if  PrintFlagsFinal is specified, at the beginning of the WebLogic server log file, you can find:

starting weblogic with Java version:
java version "1.7.0_04-ea"
Java(TM) SE Runtime Environment (build 1.7.0_04-ea-b17)
Java HotSpot(TM) 64-Bit Server VM (build 23.0-b18, mixed mode)
Starting WLS with line:
/export/home/bench/workload/target_jvm/jdk-hs/bin/java -server -Xms6400m -Xmx6400m -XX:+AggressiveOpts ...


[Global flags]

    uintx AdaptivePermSizeWeight                    = 20              {product}          
    uintx AdaptiveSizeDecrementScaleFactor          = 4               {product}          
    uintx AdaptiveSizeMajorGCDecayTimeScale         = 10              {product}          
    uintx AdaptiveSizePausePolicy                   = 0               {product}          

...
    uintx MinHeapFreeRatio                          = 0               {manageable}
    uintx MaxHeapFreeRatio                          = 100             {manageable}
     intx WorkAroundNPTLTimedWaitHang               = 1               {product}          
    uintx WorkStealingHardSpins                     = 4096            {experimental}     
     intx WorkStealingSleepMillis                   = 1               {experimental}     
    uintx WorkStealingSpinToYieldRatio              = 10              {experimental}     
    uintx WorkStealingYieldsBeforeSleep             = 5000            {experimental}     
    uintx YoungGenerationSizeIncrement              = 20              {product}          
    uintx YoungGenerationSizeSupplement             = 80              {product}          
    uintx YoungGenerationSizeSupplementDecay        = 8               {product}          
    uintx YoungPLABSize                             = 1024            {product}          
     bool ZeroTLAB                                  = false           {product}          
     intx hashCode                                  = 0               {product}           

In case you wonder what "x" means in the types intx and uintx, intx and uintx are the 'extended' int and 'extended' unsigned int types—They are 32bit wide on a 32-bit platform and 64bit wide on a 64bit platform.

Parameter Scope


The last value from the output specifies the scope of parameter, or when it can be used.  For example, if the value is
  • {experimental}
It means that you need to specify
  • -XX:+UnlockExperimentalVMOptions
to set the parameter.

Similarly, you can provide
  • -XX:+UnlockDiagnosticVMOptions
to enable the tuning of a parameter if the its scope is:
  • {C1 diagnostic} or
  • {C2 diagnostic}
C1 and C2 here relate to the JIT compiler used.  C1 is for the client VM while C2 is for the server's.

Finally,  if flags are marked as manageable, they are dynamically writeable through the JDK management interface (com.sun.management.HotSpotDiagnosticMXBean API) and also through JConsole. The manageable flags can also be set through jinfo -flag.

Test


We know the scope of  UseCriticalJavaThreadPriority parameter is experimental.  If we set it without specifying -XX:+UnlockExperimentalVMOptions first, here is the result:

$ /export/home/bench/workload/target_jvm/jdk-hs/bin/java -server -XX:+PrintFlagsFinal  
  -XX:+UseCriticalJavaThreadPriority -version >tmp.tmp
Unrecognized VM option 'UseCriticalJavaThreadPriority'
Error: Could not create the Java Virtual Machine.
Error: A fatal exception has occurred. Program will exit.

However, if you add  -XX:+UnlockExperimentalVMOptions, you will be allowed to set its value :

$ /export/home/bench/workload/target_jvm/jdk-hs/bin/java -server -XX:+PrintFlagsFinal 
  -XX:+UnlockExperimentalVMOptions -XX:+UseCriticalJavaThreadPriority -version >tmp.tmp
java version "1.7.0_04-ea"
Java(TM) SE Runtime Environment (build 1.7.0_04-ea-b17)
Java HotSpot(TM) 64-Bit Server VM (build 23.0-b18, mixed mode)

Finally, remember to add -version at the end as shown here:

$jdk-hs/bin/java -server -XX:+PrintFlagsFinal -XX:+UseSerialGC -version >tmp5.txt

For example, if you specified options in this order, it would ignore -XX:+UseSerialGC setting:

$jdk-hs/bin/java -server -XX:+PrintFlagsFinal -version -XX:+UseSerialGC >tmp6.txt

 

How about JRockit?


For JRockit, you use:
  •  ./java -Xprintflags -version

 

Notes

  • {pd product} means platform dependent

 

References

  1. HotSpot JVM Options Displayed: -XX:+PrintFlagsInitial and -XX:+PrintFlagsFinal
  2. Inspecting HotSpot JVM Options
  3. Redux: Inspecting HotSpot JVM Options
  4. Oracle® JRockit Command-Line Reference Release R28
  5. Default Values of JRockit's VM Options (XML and More)
  6. HotSpot Glossary of Terms