DEV Community

Cover image for Java Compilation Process : From Source Code to Bytecode Execution
Krunal Kanojiya
Krunal Kanojiya

Posted on • Originally published at techalgospotlight.com

Java Compilation Process : From Source Code to Bytecode Execution

Java is one of the most widely used programming languages, known for its simplicity, reliability, and platform independence. What sets Java apart from many other languages is its unique compilation process that allows code to run on any machine that has the Java Virtual Machine (JVM) installed. This makes Java applications highly portable and efficient. In this article, we'll walk through the entire Java compilation process, from writing source code to executing the program on the JVM. Understanding this process is essential for any Java developer, as it demystifies what happens behind the scenes when you compile and run a Java program.

Step-by-Step Java Compilation Process

1. Source Code Creation

The first step in the Java compilation process is writing your source code. This is where you, the programmer, create a .java file containing the instructions for the program you want to execute. The source code is written in human-readable Java syntax. Here's an example of a simple Java program, HelloWorld.java:

public class HelloWorld {
    public static void main(String[] args) {
        System.out.println("Hello, World!");
    }
}
Enter fullscreen mode Exit fullscreen mode

In this file, we define a class HelloWorld and a main method, which prints "Hello, World!" to the console. Once the source code is ready, the next step is to compile it into bytecode.

2. Compilation with javac (Java Compiler)

Java's compilation process starts with the javac command, which stands for Java Compiler. The role of the javac compiler is to take the source code (written in .java files) and translate it into bytecode, which is stored in .class files. Bytecode is a platform-independent intermediate representation of the program, and it is not directly executed by the underlying hardware.

To compile the HelloWorld.java file, you'd run the following command:

javac HelloWorld.java
Enter fullscreen mode Exit fullscreen mode

This command tells javac to read the HelloWorld.java file and output a HelloWorld.class file containing bytecode. The javac compiler performs a series of checks on the source code to ensure that it follows the syntax and rules of the Java language. If any errors are found, the compiler will fail and output error messages indicating what went wrong. Once the compilation is successful, the .class file is generated, which can now be executed by the JVM.

3. Java Bytecode and the Role of JVM (Java Virtual Machine)

The key to Java's "write once, run anywhere" philosophy is its use of bytecode and the JVM. Once the source code is compiled into bytecode, the JVM is responsible for executing it on any machine. Bytecode is not specific to any particular machine architecture, making Java programs platform-independent.

Each platform has its own JVM implementation, but all JVMs can interpret the same bytecode. When you run a Java program, the JVM reads the bytecode and translates it into machine code that your operating system and hardware can understand.

JVM Components Involved in Execution

The JVM is a powerful component of the Java runtime environment. It is responsible for loading, verifying, and executing Java bytecode. Let's explore the key parts of the JVM that are involved in this process.

1. ClassLoader Subsystem

The ClassLoader is responsible for loading .class files into memory. It reads the bytecode from disk and prepares it for execution by the JVM. One of the most important features of the ClassLoader is its dynamic class-loading capability, which means that classes are loaded into memory as they are needed during runtime.

For example, when you run the command:

java HelloWorld
Enter fullscreen mode Exit fullscreen mode

The JVM's ClassLoader loads the HelloWorld.class file into memory so that it can be executed. The ClassLoader also handles locating classes from external libraries or packages by looking through the classpath.

2. Bytecode Verifier

Once the bytecode is loaded into memory, it undergoes a verification process. The bytecode verifier checks that the bytecode follows the Java language specification and does not violate any rules, such as accessing private fields or methods. This step ensures the security and stability of Java applications, preventing potentially malicious or incorrectly compiled code from being executed.

The verifier checks for:

  • Proper use of data types.
  • Correct branching and control flow.
  • Ensuring no illegal memory access or stack overflow.

3. Runtime Data Areas

The JVM uses several runtime data areas to manage the execution of Java programs. These areas are responsible for storing variables, objects, and method information during program execution. The main runtime areas include:

  • Heap: This is where Java stores objects that are dynamically allocated. All objects are created in the heap.
  • Stack: Each thread in the Java program has its own stack, where local variables and method call information are stored.
  • Method Area: This area stores class-level information, including method and field data.
  • Program Counter: The program counter keeps track of the current instruction being executed in a thread.

4. Execution Engine

The execution engine is the part of the JVM responsible for executing bytecode. There are two primary ways the execution engine handles bytecode:

  • Interpretation: The JVM reads bytecode instructions one by one and translates them into machine code on the fly. This is slower but simpler.
  • Just-In-Time (JIT) Compilation: To improve performance, modern JVMs use JIT compilation, where bytecode is compiled into machine code at runtime. This results in faster execution since the machine code is directly run by the CPU.

Just-In-Time (JIT) Compilation

The JIT compiler is one of the most crucial components for optimizing Java's performance. When the JVM detects that certain methods are called frequently, it compiles those methods into machine code, rather than interpreting the bytecode every time. This reduces the overhead of interpretation and speeds up execution significantly.

JIT compilation is adaptive, meaning it monitors the code being executed and optimizes the most frequently run parts of the code. The result is that after a short period of interpretation, performance improves as the program runs faster with machine-level code.

Garbage Collection

One of Java's major advantages is its automatic memory management system, primarily handled by the Garbage Collector (GC). Garbage collection is the process by which the JVM reclaims memory that is no longer being used by the program. When an object is no longer reachable or needed, the GC removes it from the heap, freeing up memory for new objects.

There are several garbage collection algorithms available in Java, such as:

  • Serial GC: A simple, single-threaded collector.
  • Parallel GC: Uses multiple threads to speed up garbage collection.
  • G1 GC (Garbage First): A low-latency garbage collector suitable for large heaps.

Garbage collection happens automatically in Java, but developers can influence its behavior through configuration options.

Common Errors During Java Compilation

Java developers frequently encounter compilation and runtime errors. Some common issues include:

  • Syntax errors: These occur when there is a mistake in the source code syntax, such as missing semicolons or parentheses.

    Example:

    System.out.println("Hello, World!"
    

    The above will result in a compilation error due to the missing closing parenthesis.

  • Classpath issues: Sometimes, external libraries or dependencies are not found during compilation or runtime. This can lead to "ClassNotFoundException" or "NoClassDefFoundError."

    Solution: Ensure that all necessary libraries are correctly included in the classpath.

  • Runtime exceptions: Issues like NullPointerException occur when trying to access an object that hasn't been initialized.

Conclusion

Why I am writing this in depth article about java compilation process because its very important to know anyone who want to learn java. Without or bare minimum of knowledge make you cry in future. I tried my best to show, How actually everything works in java behind the scenes.

I will not write articles like loops, operators and etc, because this are the common things that you will able to find it your self. Instead of I will share most complicated and important topics articles to everyone with simple language. I Learned this things from TELUSKO and Prakruti Vyas 😀

Top comments (0)