All the code written in .java files is compiled into bytecode by the java compiler.
This bytecode is written into .class files.
Bytecode is a set of instructions each of which is one byte long.
Some bytecodes have operands which can make the whole instruction bigger than a byte but the instruction without the operand is only one byte long.
Since a byte is made up of 8 bits, the number of such instructions is limited to 256.
Out of these 256 combinations, around 51 are reserved for future use and around 3 are set aside to be left permanently unimplemented.
Interpretation by JVM
The 200 instructions specified by the Java byte-code is interpreted by the JVM at runtime and translated into native code.
Native code means platform specific code, for example an instruction like 'move x to y' may be implemented differently on windows than from linux and still more differently on a mobile phone.
The presence of bytecode frees the programmer from worrying about platform specifications.
Code written in java is compiled into bytecode which can be run on a JVM on any platform.
It is the JVM which emits different native instructions catering to different platforms.
That is why Java code can be written once and then run anywhere.
Note: The bytecode can not be run directly on any platform as the instruction set in it is meant to be understood only by the JVM.
Very early versions of JVM used to interpret bytecode piece by piece into native code.
This was quite slow because even repetitive code (for example, that within a big loop or within a repetitively called function) was interpreted everytime.
Then with the release of Java 1.2 in the year 1999, JIT compiler HotSpot was introduced which cached the compiled bytecode for frequently used parts of code.
Also, it continuously analyzed the running code for hot-spots and tried to optimized the same.
Advantages of bytecode
1) The JVM has to deal only with around 200 instructions and need not worry about all the myriad features offered by Java as all those are compiled by the Java compiler into bytecode.
To support a platform then, JVM only needs to add a mapping of bytecode instructions to native instructions (this is over-simplification though).
This means that a new platform can be made Java compliant very easily.
2) JIT compilation can sometimes achieve better optimization like function-inlining because it has runtime metrics available, something which is absent in a static compiler.
Since static compilers directly produce native code, they cannot determine if a function is used very frequently and should be inlined.
So runtime metrics can be used to achieve better performance optimizations with bytecode approach.
3) JVM can be made more secure by inspecting each bytecode and determining if it is secure enough to let the platform execute it.
While translating into native code, JVM can invoke policies defined in its Security Manager and allow/disallow certain operations like file manipulation for enhanced security.
4) Since the bytecode is only 200 instructions, theoretically any language can be compiled into this instruction set and thus can be supported as an executable language in Java.
Got a thought to share or found a bug in the code? We'd love to hear from you: