Understanding the java compilation and execution process
Let’s see what happens when you run the javac and java commands in the process of compiling and executing a Java program:
1. The javac Command
javac ByteCodeExample.java
1.1. Reading the Source Code
The Java compiler (javac) reads the source code from the ByteCodeExample.java file.
1.2. Parsing
The compiler parses the source code to check for syntax errors and to understand the structure of the program (classes, methods, variables, etc.).
1.3. Semantic Analysis
The compiler performs type checking and other semantic checks to ensure the code adheres to the Java language rules.
1.4. Bytecode Generation
After successful parsing and semantic analysis, the compiler translates the source code into Java bytecode. Bytecode is an intermediate representation of the code that the JVM can execute.
ByteCodeExample.java
Run the javac ByteCodeExample.java command to compile the Java source file.This command will produce a ByteCodeExample.class file in the same directory.
To view the bytecode, use the javap tool with the -c (for disassemble) option:
javap -c ByteCodeExample
Explanation of the Bytecode
§ The ByteCodeExample constructor (<init>) initializes the object and calls the superclass constructor (java/lang/Object).
§ The main method:
0: getstatic fetches the static field System.out.
3: ldc loads the constant string “Hello! I am Udaykishore”.
5: invokevirtual invokes the println method of PrintStream to print the string.
8: return ends the method execution.
This bytecode represents the low-level instructions that the Java Virtual Machine (JVM) executes to run the program.
1.5. Class File Creation
The compiler writes the generated bytecode to a .class file. For example, ByteCodeExample.class.
2. The java Command.
java ByteCodeExample
2.1. Launching the JVM
The java command launches the Java Virtual Machine (JVM).
2.2. Class Loading
The JVM uses the Class Loader subsystem to load the ByteCodeExample.class file into memory.
2.3. Bytecode Verification
The JVM’s bytecode verifier checks the loaded bytecode for validity. This step ensures that the bytecode is correct and does not violate any security constraints or access rules.
2.4. Linking
Linking involves combining different sections of the program. It includes:
Verification: Ensures bytecode adheres to the JVM specification. Confirms the structure and type safety of the bytecode.
Preparation: Allocates memory for class variables. For ByteCodeExample, since there are no class variables (only a local variable in the main method), the preparation step is straightforward. However, if there were static fields, the JVM would allocate memory for these fields and initialize them to their default values (e.g., 0 for integers, null for object references).
Resolution: Converts symbolic references into actual memory references. Converts symbolic references for System.out and PrintStream.println into actual references. Ensures that the correct methods and fields are accessed during execution.
2.5. Initialization
The JVM initializes the class by executing static initializers and initializing static variables.
2.6. Execution
The JVM starts executing the main method of the ByteCodeExample class. Execution involves:
Interpreting: The JVM interprets the bytecode instructions.
Just-In-Time Compilation (JIT): Frequently executed bytecode sections might be compiled into native machine code for performance optimization.
2.7. Running the Program
The System.out.println(“Hello! I am Udaykishore”); statement in the main method is executed, which prints “Hello! I am Udaykishore” to the console.
2.8. Garbage Collection
The JVM performs automatic memory management through garbage collection, reclaiming memory used by objects that are no longer needed.