In one of the previous lessons, we learnt that one of the main reasons for Java’s popularity is the language’s “Write once, run (any)everywhere” paradigm. This is many a times referred to by acronym WORA. In this lesson we examine all the steps in Java source code execution. These steps contain the magic sauce for enabling WORA in Java.
Java achieved the magical feat of WORA by harnessing one of the oldest principles of computer science – indirection. The technique refers to the process of solving a problem by adding layers of abstraction. The designers of Java used this technique and added a layer of bytecode generation followed by code execution. This execution occurs in what is known as Java Virtual Machine or JVM for short. We already covered the relationship betweeen JDK, JRE, JVM and JIT. We have also taken a sneak peek at what’s inside JVM.
In a nutshell, WORA is made possible by Java’s unique (at least at the time it first came out) program execution model. Java’s program execution model is divided into two distinct stages –
- Bytecode execution
These two stages are not directly related to each other. In fact, most of the times the second step occurs on a different machine and usually long after the first step. Let’s look into each of these steps in more detail.
Compiling a Java Program
Compilation, in case of Java, refers to the process of invoking javac on .java source files to generate .class files.
As shown in the diagram above, the steps are as follows –
- User writes syntactically correct Java program. If the user intends to run the code, the program should have at least one main method with the signature of
public static void main(String… args)
- Installs JDK. Refer to this lesson.
- Invokes javac on the java files to generate .class. The class files contain bytecode.
Running a Java Program: Bytecode execution
Running a Java program essentially involves invoking java on the class file generated in the previous step. Once the bytecode is handed over to the JVM for execution, the following things happen –
- Classloader subsystem starts loading the class file handed over to it.
- The classloader also loads the dependent class files. These are the files that are imported.
- The loaded bytecodes are checked for syntactic validity by the verifier module. The verifier ensures that Java bytecode syntax is not violated and the bytecode loaded is safe to execute.
- Memory areas such as method area and heap are initialized.
- The execution engine reads the bytecode and runs it line by line, barring of course the JIT compiler optimization.