Engineering Full Stack Apps with Java and JavaScript
In Java, we write code in .java files and are referred to as source files. Java application source files are compiled; but instead of being compiled into a specific binary for a specific CPU, they are compiled into an optimized architecture neutral assembly language know as Java bytecodes, and are saved in .class files. These class files (Java bytecode) are then loaded and run by the Java Virtual Machine (JVM), similar to other interpreters. Unlike other interpreters, Java can compile the bytecode again to platform specific binary with additional optimizations during execution; and this is possible as Java is executing an optimized binary, not a high level source code. Since this compilation occurs during program execution, it is called Just-In-Time compilation.
Oracle’s standard JVM implementation (that we download from oracle downloads page) is known as Hotspot JVM. Hotspots are sections of code that are executed more than other sections of code. When the JVM executes code, it does not begin compiling the code immediately. It would not be worth to compile and execute a section of code which is executed only once or very less times; instead it would be better to interpret the bytecode directly without compiling in those cases. Compiling a frequently executing section of code (hotspot) will be worth as the extra CPU cycle time taken for compilation can be overcome during subsequent runs. Also, more the JVM executes sections of code it can figure out more optimizations that can be made.
There are 3 versions of the JIT compiler, and they are:
To some extent, you can choose the compiler you want to use by supplying an argument to java (-client, -server or –d64). However different Java binaries (32 bit for Linux, 64 bit for Windows etc.) contain different compilers and may also different behavior for these flags. For instance,
o Solaris 64-bit is an exception where -client will give 32 bit client, -server will give 32 bit server and –d64 will give 64 bit server, as expected.
Now we will see differences between client and server, and then 32-bit and 64-bit.
A JVM does not immediately compile the bytecode to platform specific binary while executing. A hotspot JVM will find and compile hotspots and execute the rest directly from bytecode. Hotspots are sections of code that are executed more frequently than other sections. The more the JVM get time, the better hotspots it can find and hence better optimization.
The client compiler begins compiling the bytecode sooner than the server compiler does. Therefore, during the beginning of code execution, the client compiler will be faster, and in the long run, a server compiler would have saved more time, as ultimately, code produced by the server compiler will be faster than that produced by the client compiler.
There is also another variant of compilation which is a combination of these: tiered compilation. With tiered compilation, code is first compiled by the client compiler; and later as it becomes hot, it is recompiled by the server compiler. Starting from Java 7u4, tiered compilation usually offers the best performance for an application. Still, tiered compilation has a few concerns in Java 7, and so it is not the default setting. To use tiered compilation, use the flag -XX:+TieredCompilation (the default value of which is false) along with server compiler. In Java 8, tiered compilation is enabled by default if server compiler is the default.
For a 32-bit operating system, you can only use a 32-bit version of the JVM. For a 64-bit operating system, you can choose to use either the 32- or 64-bit version of JVM. In certain situations, like when heap space is less than 3GB and the total process space (includes the heap, permgen, native code and native memory) is less than 4 GB, then 32-bit JVM might give a better performance over the 64-bit version, because, the memory references within the 32-bit JVM will be only 32 bits, and manipulating them is less expensive than manipulating 64-bit references.
You can find your JIT versions as:
java –version
In my personal system this gives, below output:
java version "1.7.0_51"
Java(TM) SE Runtime Environment (build 1.7.0_51-b13)
Java HotSpot(TM) Client VM (build 24.51-b03, mixed mode, sharing)
And even check if version is changing with options as:
java –server –version
This will give the output as:
java version "1.7.0_51"
Java(TM) SE Runtime Environment (build 1.7.0_51-b13)
Java HotSpot(TM) Server VM (build 24.51-b03, mixed mode)
And if I try an invalid option (-d64 is invalid for 32-bit) as:
java –d64 –version
This will give the output as:
Error: This Java instance does not support a 64-bit JVM.
Please install the desired version.
You can play around with these options in your machine and understand better.
For further learning and examples, you can refer the book Java Performance: The Definitive Guide by Scott Oaks.