Level: Introductory Nikolay Yevik (yevik@us.ibm.com), Linux on POWER Technical Consultant, , IBM
30 Apr 2004 This paper introduces you to the Just-In-Time (JIT) compiler and to the Mixed Mode Interpreter (MMI) optimizations techniques, used in IBM’s JVM 1.3.1 and 1.4.1 and to their potential effect on Java applications that are being migrated from Sun Hotspot JVM.
Introduction
The "write once, run anywhere" (WORA) principle applies only when migrating pure Java™ byte-code from a Java Virtual Machine (JVM) of a certain version on one platform to a JVM of exactly the same version on a different platform.
However, sometimes this transition process does not happen seamlessly. One of the factors that can influence migration process is the difference in internal implementations of optimization techniques between JVMs from different vendors.
When migrating a Java application from a platform where Sun JDK were used to IBM JDK it is important to be aware of differences in optimization techniques used in JVMs from the two vendors, how these differences can affect an application, and how to gain performance advantage by tuning optimization mechanisms available in IBM JVM.
Paper focuses mainly on JIT diagnostics in case of encountering problems with JIT when migrating from Sun HotSpot JVM to IBM JIT-based JVM.
Most of the discussion presented in this paper applies to JVM 1.3.1 and 1.4.2 from IBM in general, but specifically addresses IBM JVM 1.3.1 and JVM 1.4.2 for Linux, including Linux on POWER and PowerPC architectures.
In JVM 1.3.1 from Sun both JIT and HotSpot compilers were included and could be invoked with -server or -hotspot (by default meaning client HotSpot VM) for HotSpot, or -classic for the JIT, with -hotspot option being implicitly default. With Sun JDK 1.4.1 and later, only HotSpot is available. IBM JVMs 1.3.1 and 1.4.2 use combination of JIT and MMI that is the default runtime mode, to achieve the same goal as HotSpot.
For comprehensive information about IBM JVM specifics please refer to IBM developer kits - diagnosis documentation at http://www-106.ibm.com/developerworks/java/jdk/diagnosis/.
Sun Java HotSpot VM
The Java HotSpot VM is Sun Microsystems’s JVM implementation. The Java HotSpot VM adheres to the same specification as the Java II Platform VM, and runs the same byte codes, but it has been re-engineered to leverage new technologies like adaptive optimization to improve performance of the JVM. The Java Hotspot does not include a plug-in JIT compiler but instead compiles, inlines, and performs other optimizations on methods that appear to it as being the most used in the application -"hot spots". This means that on the first pass through the Java byte-code is interpreted there is no byte-code to native code compiler (like JIT) present. If the code then appears as being a hotspot in your application the HotSpot compiler compiles the byte-codes into native code, which is then stored in a cache, and performs other optimizations at the same time. One advantage to selective compilation over JIT, that is used by itself - without adaptive optimization mechanism of some sort, is that the byte-code to native code compiler can spend more time generating highly optimized native code for areas that would benefit from the optimization most, leaving aside code that may be best run in interpreted mode.
 |
IBM JVM JIT and MMI
The tool used to increase the performance of Java application is the Just-In-Time (JIT) compiler. A JIT is a code generator that converts Java byte-code into native machine code of the host platform. Java programs invoked with a JIT generally run much faster than when the byte-code is executed by the interpreter.
When JVM starts, a great number of methods are being loaded into JVM and executed.
If JIT is disabled JVM starts quickly but in most cases runs slowly as all byte-code is being interpreted. If JIT is enabled but used without adaptive optimization mechanism of some kind JIT tries to compile all methods at a startup time. For small applications, like applets, it might come to a point when it takes longer to start JVM than to run an application.
The IBM JVM Mixed Mode Interpreter (MMI) that is tightly coupled with JIT alleviates this problem. It serves the same purpose as HotSpot VM, which is to spread the compilation of methods out over the lifetime of JVM. Among other things, MMI keeps count of how many times a certain method has been executed. MMI has a default threshold count that was carefully chosen, through extensive testing and research, for each platform IBM JVM delivered for to achieve balanced performance.
Every time a method executes its threshold count in MMI is decremented. When the threshold count for a method reaches zero (0) the method becomes eligible for compilation into native code by JIT. Thus, the same as with HotSpot VM, high-usage methods -"hot spots", are being compiled by JIT soon after JVM starts, while low-usage methods are compiled later or perhaps not compiled at all during the lifetime of JVM process.
IBM JVMs 1.3.1 and 1.4.2 also provide a non-standard option -Xquickstart for improving startup time of some Java applications. The -Xquickstart option causes the JIT to run with a subset of optimizations; that is, a quick compile. This option is appropriate for shorter running applications, especially those where execution time is not concentrated into a small number of methods - "hot spots". The -Xquickstart option can degrade performance if it is used on longer-running applications that contain hot methods.
As with any other non-standard -X options, implementation and presence of -Xquickstart option is subject to change without notice.
The MMI threshold count can be lowered to speed up JIT compilation of a method or increased to achieve an opposite effect. This is done through the use of an environment variable IBM_MIXED_MODE_THRESHOLD that as any other environment variable needs to be set in the shell instance that JVM process will run in. Naturally, this variable is set before JVM process started and is effective for all methods executed in JVM.
Setting IBM_MIXED_MODE_THRESHOLD=0, effectively disables MMI, as all methods will be immediately JIT-compiled the first time they are loaded into JVM.
IBM JVM runtime modes are:
- Both MMI and JIT are enabled
This is the default IBM JVM setting.
- MMI disabled, JIT enabled
All methods are compiled before being run for the first time, meaning that startup time of the JVM can be slow, but later performance will be good.
- MMI and JIT both disabled
The JVM is a pure interpretive system. No code is compiled. Turning JIT off automatically disables MMI as well, while setting IBM_MIXED_MODE_THRESHOLD to zero (0), and thus effectively disabling MMI, does not disable JIT
The MMI is a highly effective interpreter, hand crafted in assembly code of a host platform to achieve best possible optimizations. Though JIT is not an integral part of JVM, and is provided in form of a shared library (libjitc), it is tightly coupled with MMI, and the two are tightly coupled with IBM JVM.
When migrating Java applications from Sun JVM 1.3.1 that was used in Java HotSpot VM regime, or even in classic - JIT mode, as Sun’s JIT implementation is not entirely the same as IBM’s JIT, or from Sun HotSpot JVM 1.4.1, where no JIT is available at all, to IBM JVM, problems might arise in rare cases, ranging from performance degradation, to code producing incorrect results, to JVM hangs, crashes, and exceptions.
JIT diagnostics
This section describes techniques used to debug and diagnose IBM JVMs’ JIT and MMI when problems encountered. The previous section introduced JIT that is an essential part of IBM JVM.
At any given time the JVM process consists of the executables and a set of JIT-compiled code that is dynamically linked to the MMI method wrappers that are in the JVM. The JIT compiled native machine code is placed into the JVM native data memory segment thus increasing native memory footprint of the JVM process, and the MMI wrapper is changed to point to the compiled code.
JIT therefore has a great influence on the flow of execution of a Java program.
In case application being migrated from another platform encounters problems such as:
- Deadlock hangs
- Consistently incorrect results
- Inconsistent results
- Abnormal termination
- Infinite busy looping
- Memory leak
the very first thing that should be considered as the possible cause of the problem is JIT.
It is important to determine whether JIT is the root cause of the problem as soon as possible during problem determination for three reasons:
- Problem is likely to be caused by JIT given its active role in JVM
- JIT debugging is very different from other types of problem determination techniques
- JIT debugging process might be time-consuming and complex usually requiring high level of technical expertise.
Determining if this is a JIT problem
In some cases it's clear from the nature of the problem that the JIT is at fault. For example, in cases when JVM terminates with a Javadump (on Linux file name for Javadump is of format javacore.YYYYMMDD.HHMMSS.PID.txt ) or a with a Linux core, it might be clear from a stack trace in Javadump or output of gdb ran against the java executable and the core file that JIT is the cause. In some cases it is directly shown that signal that brought JVM process down was received in libjitc.so. In other cases, crash or a hang happens in code that is in the JVM process space yet outside the range of compiled code in that process, that would indicate a problem with JIT-compiled code.
In most cases however, there are no clear indications whether JIT is the source of a problem. Therefore, given the importance of JIT, the very first step in every problem determination should be disabling JIT, except for the cases when it is clearly not a JIT-related problem. Even in cases when there are indications that JIT is at fault, it is better to verify that by disabling JIT and rerun of an application with JIT disabled.
To disable JIT, first check the current setting of the environment variable JAVA_COMPILER, and then set it to NONE. For example, for the Bourne Again Shell (bash) or the Korn shell (ksh):
export JAVA_COMPILER=NONE
|
for csh:
setenv JAVA_COMPILER NONE
|
Another way to disable JIT is to pass the -D option on the java command to set java.compiler to NONE to override the default or the environment variable settings:
java -Djava.compiler=NONE <myapp>
|
JIT is enabled by default. To verify if JIT is enabled or to not pass -version to java command:
If the JIT is not in use, a message is displayed that includes the following:
If the JIT is in use, a message is displayed that includes the following:
If JAVA_COMPILER="" or -Djava.compiler="" is specified, the JIT compiler is disabled. If JAVA_COMPILER is not set, as in:
the JIT compiler is enabled.
Run the program again to see if problem reappears with JIT disabled. If it does, this is not a JIT related problem. If problem does not exhibit itself with JIT off, then it might be a JIT related problem. The fact that problem does not surface with JIT off does not necessarily mean that JIT compiler is at fault. For example, methods in highly threaded timing-dependent applications may run at different speeds when compiled than when interpreted, so that a logical error in Java code is only exposed when that code is compiled.
To enable the JIT, set the JAVA_COMPILER to jitc or switch the JIT compiler on through the command line:
java -Djava.compiler=jitc <myapp>
|
or unset JAVA_COMPILER or remove -Djava.compiler from options passed to java.
Dealing with a JIT related problem
Normally, a JIT-related problem warrants a service call to an appropriate IBM support component. JIT diagnostics can be a time consuming procedure requiring high level of technical expertise. Usually there is no defined algorithmic approach to solving a JIT related problem due to innate complexity and dynamic nature of the JIT, so it is better to be guided by an experienced service representative in JIT debugging efforts. However, it is better to confirm that the problem really lies within JIT before contacting IBM service, as the fact that application runs with JAVA_COMPILER=NONE correctly, does not definitively points to JIT as the root of the problem. Disabling JIT completely with JAVA_COMPILER=NONE, can have significant adverse performance effect on an application. In some benchmark tests, version with the JIT completely disabled (JAVA_COMPILER=NONE), was over eight times slower than the standard JIT-enabled run. This can be unacceptable in production environment. Therefore, it is better to have a production system clone, where testing and debugging can be done. There exists extensive documentation on JIT debugging that can be provided by IBM service representative along with instructions relevant to a particular JIT problem.
This section is not a comprehensive guide to JIT diagnostics, but introduces some basic techniques that would aid in JIT debugging efforts to achieve the following goals:
- Confirm that the problem is really with JIT before contacting IBM support
- Find a workaround that serves two purposes:
- provides temporary workaround with acceptable performance in production environment, while IBM works on fixing the root cause of the problem
- helps IBM in problem determination
- Gather information required by IBM support for JIT problem determination.
If you cannot resolve a problem in a reasonable amount of time, if the problem is production critical, or if you already have a simple testcase that consistently reproduces the problem, please contact IBM service immediately.
First and foremost, before you embark on JIT debugging, you need to make sure that problem happens on the most recent patch level of JVM (like JVM 1.3.1) that is being used or on a newer JVM (like JVM 1.4.2) version with latest fixes applied, provided application's compatibility with the later version of JVM.
There is no value in spending effort on JIT debugging that might turn out to be time-consuming, depending on a complexity of a particular case, to pinpoint a JIT problem that has already been fixed in an available latest fixes pack for the same JVM. JIT is constantly evolving and there is a good chance that the problem you are experiencing has been already fixed in the latest fixes pack.
Secondly, if the problem happens with JIT completely off (JAVA_COMPILER=NONE) on a multiprocessor system it is helpful to see if the problem persists with JIT off on a single-processor system. If the problem does not happen on a single-processor system with JIT off, that might be an indication that JIT is not at fault. If the problem does not happen at all on single-processor system even with JIT enabled, then it is likely to be a problem not related to JIT, but to time-dependent logic error in application. This information needs to be provided to IBM support at the time of service request.
To see how many processors are on the Linux system and what their characteristics are execute:
Aside from taking an application from a multi-processor system and testing it on a
single-processor one, a multi-processor machine can be converted into a single-processor machine by disabling all the processors but one. The ways this can be achieved are hardware and firmware specific. The easier way is to set the processor affinity for our process to a single processor. With newer Linux distributions, such as Red Hat Enterprise Linux 3 and SUSE Linux Enterprise Server 9, there is a user space interface to the system calls sched_getaffinity() and sched_setaffinity() defined in sched.h. Using these functions and some macros defined in sched.h, a user can manipulate the CPU affinity of a process from the user space by specifying the CPU set of the GNU extension type cpu_set_t. Through the use of this interface you can set up the CPU affinity set of a process to just one processor and see whether your JIT problem can be reproduced on a simulated
one-processor machine.
There are three main steps in JIT debugging to achieve a viable workaround giving acceptable performance that would also point IBM support to the root cause of the problem:
- Isolating the package/class/method that causes problem when its code is optimized and JIT-compiled into a machine code and skipping JIT compilation of that code.
The goal is to narrow down to as few methods as possible that cause JIT problems .
Isolating JIT problems to a narrow set of methods or better yet to a single method, if only a single method is involved, a workaround is provided that will have minimal adverse performance effect on an application and will point IBM support more accurately to Java code causing the problem when JIT-compiled.
- Isolating down to as few JIT optimizations as possible and as specific as possible in the hierarchy of JIT optimizations that perform faulty optimization on the methods determined above, and disabling them for those methods.
The more specific JIT optimization, that was narrowed down to, the less adverse performance effect disabling of this optimization option will have as a workaround.
- JIT debugging procedure therefore finds the narrowest possible set of application's methods involved and faulty JIT optimization (or narrow set of optimizations) as deep in JIT optimizations hierarchy as possible. Combining the two - that is disabling that particular optimization (or limited set of optimizations) on a determined limited set of methods involved, or just one method, if only one method's code is involved, will provide a workaround with minimal possible performance degradation that is usually negligible.
There is a balance to be achieved in terms of effort involved in finding a workaround. At certain point during a JIT debugging procedure a workaround can be reached that gives acceptable performance. If goal is just to find a quick workaround with acceptable performance debugging effort can be stopped at that point.
However, if a service call to be opened with IBM support, not narrowing down enough to the narrowest possible set of methods and JIT optimizations involved will complicate resolution of a problem. In this case providing a simple test case not containing any proprietary confidential source code that reproduces the problem to IBM support will be the best approach. Having a simple straightforward test case consistently reproducing the problem is always preferable, as it significantly speeds up resolution of the problem.
Finding a method or a limited set of methods that introduce the problem when JIT-compiled should come before finding the faulty JIT optimization.
Quick workaround procedure:
1.1 set JITC_COMPILEOPT=NBUILTIN:NCHA:NMMI2JIT
/* if NBUILTIN:NCHA:NMMI2JIT passes */
1.1.1 set JITC_COMPILEOPT=NMMI2JIT...disable on-the-fly hot JITC compilation
1.1.2 set JITC_COMPILEOPT=NBUILTIN...disable build-in precompiled methods
1.1.3 set JITC_COMPILEOPT=NCHA...disable class interrelation analysis
1.2 set JITC_COMPILEOPT=NINLINING...disable method inlining
1.3 set JITC_COMPILEOPT=NQOPTIMIZE...disable intermediate code optimizations
/* if NQOPTIMIZE passes then try the options below */
1.3.1 set JITC_COMPILEOPT=NQCOPYPROPA...disable copy propagation
1.3.2 set JITC_COMPILEOPT=NQCOMMONING...disable advanced commoning
1.3.3 set JITC_COMPILEOPT=NQDEADSTORE...disable dead store elimination
1.4 set JITC_COMPILEOPT=NPRIVATIZE...disable localization of thread-safe objects
/* New options for JVM 1.4.0 and later */ 1.5 set JITC_COMPILEOPT=NOTHER
1.6 set JITC_COMPILEOPT=GLOBAL |
Consider the above procedure as pseudo code. You set the same variable JITC_COMPILEOPT to different values one by one, and after every setting rerun to your application and see if the problem persists. You need to follow statements with deeper indentation only when the previous "if" condition has been satisfied, and then follow statements with the same indentation level up until an option that allows the program to pass without a problem.
For example, if the setting JITC_COMPILEOPT=NALL does not resolve the issue, you do not follow the rest of the procedure, otherwise proceed to 1.1, and then to statements with deeper indentation if it passes, otherwise jump straight to next statement with the same indentation level, until the problem is circumvented or you have tried all the options.
There exist many more JIT optimizations than listed in the above procedure. JIT optimizations are hierarchical. That is, for example, setting JITC_COMPILEOPT=NALL, disables most optimizations except basic optimization, and includes most of all other optimizations. That is, it is a parent of other optimizations, and setting JITC_COMPILEOPT=NALL, mean that all other JITspecific optimizations components are also disabled.
To see a complete list of JIT optimizations in JVM release 1.4.2 set JITC_COMPILEOPT=TITLE(all) and execute a simple Java code, like a canonical "Hello World".
In most shells special characters such as:
" ' ` $ & | ; ( ) * ? \ > <
|
need to be preceded with escape "\" character.
You can also simply enclose the entire variable value in single quotation marks (') as long as you precede any actual single quotation marks in the variable with a backslash (\').
A list of all possible JVM 1.4.x JIT options and their internal hierarchical relations will be printed out. This list can be used in to narrow problem down to an optimization (or a small set of specific optimizations) that is at fault.
The JITC_COMPILEOPT=TITLE option is also very useful if you need current settings to be printed to standard error. This helps ensure that the options you think are being set are actually being received and recognized by the JIT compiler.
To reduce the time it takes for the error to occur you might want to set up IBM_MIXED_MODE_THRESHOLD=0. MMI is disabled and each method is compiled the very first time it is encountered, and problemsmay surface earlier. Of course disabling MMI forces all methods to be compiled as they are encountered thus compiling many methods that are not hot methods and are being invoked only a small number of times. Because of the compilation overhead involved this may actually increase the amount of time it takes for your program to run up to the point where problem occurs. You might want to experiment with a range of values to find a value where error occurs relatively quickly.
Setting JITC_COMPILEOPT variable to disable a certain JIT optimization without specifying which package, class, or method is to be disabled means that it will be disabled for "all" of the application's code thus having application-wide adverse performance effect. That is why the above procedure is suggested only as a quick workaround. Main stress should be made on isolating the method or set of methods that when JIT-compiled introduce the problem.
How to find a failing method?
Set JITC_COMPILEOPT=COMPILING and run your program again. After you set this option, the JIT compiler prints two messages for each method as it compiles the method. For example:
Compiling {java/lang/MyClass}{MyMethod}
0x0BF025C0 {java/lang/MyClass}{MyMethod}
|
The first message Compiling {java/lang/MyClass}{MyMethod} indicates that the JIT compiler has started compiling the method. The second message indicates that compilation has completed, and the hexadecimal address represents the starting offset of the machine code that will be executed each time the method is invoked.
If you have a messages in the form that following, the "F" indicates that that the method did not compile.
Compiling F {package/class}{method}
|
If the method you suspect of failing has an "X" to the right of its name, the "X" indicates that an MMI2JIT transfer took place, in which method compilation was started when a loop was detected, and execution of the method switched from interpreted (MMI) mode to compiled (JIT) mode within one invocation of the method. For example:
Compiling {java/util/MyClasss}{MyMethod} X |
There are three possible scenarios for looking at the last message other than program output:
-
0x012345678 {package/class}{method}: Crash occurred during execution of a compiled method (not necessarily the one listed with this message).
- The program continues to run, but performance is slow, or it takes too long to determine which of the above is the last message before the crash.
-
Compiling {package/class}{method}: Crash occurred while compiling the method.
In case 1, the likely cause is that the JIT compiler produced compiled code for a method, and that compiled code caused an error. You may be able to determine which method caused the failure by examining the address where the failure occurred.
If you can obtain a failing instruction address, follow these steps to isolate the failing method:
- Change the COMPILING option to COMPILING(verbose). This will result in output containing both the starting and ending addresses of the compiled code for each method. If you have narrowed the COMPILING option down to a class/method specification, use COMPILING(verbose){class-spec}{method-spec}.
- Run the program again, but capture standard output to a file, for example:
java -options classname >compiling.log
|
- When the error occurs, use the gdb debugger to find the failing address again, depending on your operating system (you need to repeat this step for this invocation because the failing address may change from one invocation to the next).
- Start the gdb debugger in the directory where the core file was created:
- If the gdb debugger is able to read the core file, it indicates the address where the failure occurred (for example, Segmentation fault in function_name at 0x10001234).
- To obtain a stack trace, use the bt command:
- Quit gdb with the quit command.
Make sure that the core file size, as shown by ulimit -c is not set to 0, or a insufficient size, as in former case core file will not be produced at all and in the latter case it might be truncated.
- Search through the
compiling.log file for a function with a starting and ending hexadecimal address that surround the failing address. Note that the starting hexadecimal addresses are usually in ascending sequential order, but not necessarily so.
- If you find a function address range that covers the failing instruction address, the message for that range indicates the method where the error was likely introduced.
In case 2 the failing instruction address is not know, therefore other techniques are required to narrow down to a failing class/method pair:
- Inspect the Java stack trace if one is printed (or written to javacore.txt) before the program crashes. The methods listed near the top of the stack trace are prime candidates.
- Add the instrumentation code in the suspected Java methods if you have access to the source code, and make note of which suspect methods did not start, or do not complete. In some cases, adding a
System.out.println() in a suspected method may cause the problem to disappear because the byte-code and the resulting compiled code for that method change sufficiently to alter the flow of optimizations applied to the method. In this situation, treat the method where the println() occurs as a suspected method. If you inserted println() calls in each of a number of suspect methods, try removing println() calls until you determine which one is preventing the failure; the method containing that println() call is a likely suspect.
- Use the debug version of
libjitc.so - libjitc_g.so
The debug version of the JIT library contains calls to the assert() function of the C library. These calls are placed in the JIT compiler code to assert that a required condition is true; when an assert call fails, it indicates that the JIT compiler code encountered a situation that was not anticipated. Often an assertion failure occurs during compilation of a failing method, whereas with the non-debug version of the JIT library, the failure may not surface until a particular instruction in the compiled method is actually executed much later. When you use the debug version of the JIT library, your program is more likely to fail right after the Compiling message for the problem method has been printed.
Debug versions of executables and shared libraries included with JDK and not JRE.
To use the debug version of the JIT library, follow these steps:
- Check that the required debug library is present in one of the directories pointed to by the
-Dsun.boot.library.path option of the java command. Check for the shared library libjitc_g.so
- Set the environment variable JAVA_COMPILER=jitcg before invoking the java command.
If you get a message:
Warning: JIT compiler "jitcg" not found. Will use interpreter
|
check that the library path you specified (-Dsun.boot.library.path) is correct and that the specified debug library is indeed in one of the directories in that path.
When you run this version of the JVM, execution stops with an assertion failure message if any assertion failure is encountered.
In case 3, or when the method fails to compile, when you see:
Compiling F {package/class}{method}
|
you can skip the method in question from being JIT-compiled.
You can disable JIT compilation of a failing method using the SKIP option of the JITC_COMPILEOPT variable.
For example:
- JITC_COMPILEOPT=SKIP{failingPackage/failingClass}{failingMethod}
- Skips JIT compilation of a particular method in a particular class.
- JITC_COMPILEOPT=SKIP{failingPackage/failingClass}{*}
- Skips JIT compilation of all methods of a class.
- JITC_COMPILEOPT=SKIP{failingPackage/*}{*}
- This skips all methods of all classes in a failing package.
The same technique of skipping methods can be applied on a broader basis as a brute force approach to isolating failing method or a set of methods if all other above techniques to find a failing method fail.
The JITC_COMPILEOPT variable allows usage of multiple options separated by a dot ( . ) on Linux and other Unix systems and usage of wildcards with regular expressions. For example:
- JITC_COMPILEOPT=COMPILING:SKIP{com/class1/*}{*}{com /class2/*}{*}
- Skips the JIT compilation of all methods of all classes of packages our firm.empdata and ourfirm.orderentry, having JIT compilation information printed out as defined by the "COMPILING" option.
- JITC_COMPILEOPT=SKIP{myClass?2}{myfunc?b}
- Skips all methods whose name is
myfunc?b, where ? is any single character, in any class whose name is myClass?2, where ? is any single character.
- JITC_COMPILEOPT=SKIP{[ab]?[de]*}{*}
- Skips all methods of all classes with first letter is 'a' or 'b' and third letter is 'd' or 'e'.
Boolean OR and NOT operations are supported by the | and ^ operators respectively. Note that these characters have special meanings in some command shells, and require escape sequences or quotation marks.
- JITC_COMPILEOPT=SKIP{java*|sun*}{*}
- Skip all methods of all classes starting with java or sun.
- JITC_COMPILEOPT=SKIP{^[js]*}{*}
- Skip all methods of all classes except those starting with 'j' and 's'.
By the end of this procedure you should be able to refine down to a narrow set of methods or, ideally, just a single method that allows to circumvent the problem when all JIT optimizations are disabled for that method or narrow set of methods.
How to find a faulty JIT optimization?
By the end of the above section you should have already determined that skipping selected methods, classes, or packages prevents the problem from occurring. In this section, you will find a faulty JIT optimization, that, when performed on methods or single method determined in above section, introduces the problem. You might have already determined a high level set of optimizations if you followed quick workaround procedure in section 3.2 that finds a workaround by finding a high-level, inclusive set of optimizations to disable and thus circumvents a problem. First, you change your SKIP options to NALL options, to determine whether basic compilation is the cause of the problem, or whether optimizations are causing it. NALL disables most optimizations without disabling basic compilation.
If your SKIP option contains more than one class-specification/method-specification pair, you may want to switch from SKIP to NALL one set at a time. While this may make solving your problems more time-consuming, it will give you a more targeted set of solutions, make it easier for IBM to fix any JIT problems in a future update when you ask for support, and give you the best performance possible for each of your classes while you wait for a program fix.
For example, if you have narrowed the failing methods down to:
SKIP{MyPackage/MyClass}{MyMethod}
{MyPackage2/MyClass2}{MyMethod2}
|
You can break this into a SKIP and a NALL option as follows:
export JITC_COMPILEOPT=NALL{MyPackage/MyClass}{MyMethod}:
SKIP {MyPackage2/MyClass2}{MyMethod2}
|
Note that the line breaks above are for formatting of this document only. There should be no spaces or line breaks anywhere in the setting of JITC_COMPILEOPT.
Run the program again. If it runs successfully you need to refine optimizations. You might follow the procedure in section 3.2 for finding a quick workaround that goes through disabling most inclusive high-level optimizations one by one. It can be refined even further by going into a hierarchy of particular optimization.
If it does not run, there might be other reasons beyond discussion presented in this document. You have a workaround by skipping selected methods from full JIT-compilation altogether.
At this point IBM service can be contacted.
Combining results together
The final result of debugging procedure should be a narrow set of optimizations disabled on a narrow set of methods. In simplest case, there is only one faulty optimization performed on a single method.
For example, if you determined that the problem lies when inlining is performed on method myMethod of MyPackage/MyClass, then final workaround takes form (Korn Shell):
export JITC_COMPILEOPT=NINLINING{MyPackage/MyClass}{myMethod}
|
Current IBM JVM offerings for Linux
At the time of this writing JDK and JRE for Linux can be downloaded from IBM developer kit for Linux at http://www-106.ibm.com/developerworks/java/jdk/linux140.
The latest IBM JDK release for Linux on POWER at the time of the writing is 1.4.2
Conclusion
This document introduced you to optimization techniques used in IBM JVM, – JIT coupled with MMI.
The brief JIT diagnostics procedure presented in this document helps to:
- confirm that JIT is the cause of a problem
- restore system quickly to production acceptable performance
- gather information required by IBM to fix the root cause of the problem, when JIT-related problem is encountered during migration of Java application from non-JIT-based JVM to IBM JVM.
Resources
About the author  | |  | Nikolay Yevik, a Linux on POWER consultant in IBM’s Solutions Enablement group, has more than 6 years of experience working on UNIX platforms, performing development work in C, C++ and Java. He holds Masters degrees in Petroleum Engineering and Computer Science. Nikolay can be reached at yevik@us.ibm.com.
|
Rate this page
|