Skip to content
  • Compilation, Bytecode & Class Loading · Java

Compilation, Bytecode & Class Loading

LearnViaNotes @learnvianotes· 3 min read

javac turns each class into a .class file of bytecode; the JVM loads classes lazily, on first use, through a chain of class loaders.

Main.java → javac → Main.class (bytecode) → Class loader → Bytecode verifier → Interpreter / JIT → Native CPU

Bytecode

You can read the bytecode with javap -c:

Terminal
$ cat Add.java
class Add { static int add(int a, int b) { return a + b; } }
$ javac Add.java && javap -c Add
  static int add(int, int);
    Code:
       0: iload_0        // push local 0 (a)
       1: iload_1        // push local 1 (b)
       2: iadd           // pop two ints, push their sum
       3: ireturn

Class loading: load → link → initialise

  1. Loading

    A class loader finds the bytes (from the module image, a JAR, the file system, the network…) and creates a Class object.

  2. Linking — verify

    The bytecode verifier checks the code is type-safe and can't corrupt the stack or memory.

  3. Linking — prepare

    Static fields are allocated and set to default values (0, null, false).

  4. Linking — resolve

    Symbolic references (names of other classes, methods) become direct references (may be lazy).

  5. Initialisation

    Static initialisers and static field assignments run, top to bottom — exactly once, on first active use.

The class loader hierarchy

LoaderLoadsNotes
BootstrapCore modules: java.base (java.lang, java.util…)Written in native code; appears as null from Java
Platform (was Extension)Other Java SE / JDK modules (java.sql…)Parent is bootstrap
Application (System)Your classes from the class path / module pathParent is platform
CustomAnything you want (plugins, hot reload, app servers)Extend ClassLoader
Java
public class Main {
    public static void main(String[] args) {
        System.out.println(String.class.getClassLoader());              // bootstrap → null
        System.out.println(java.sql.Date.class.getClassLoader().getName());
        ClassLoader app = ClassLoader.getSystemClassLoader();
        System.out.println(app.getName() + " -> parent " + app.getParent().getName());
    }
}
Output
null
platform
app -> parent platform

Lazy initialisation — classes load on first use

Java
public class Main {
    public static void main(String[] args) {
        System.out.println("main starts");
        System.out.println(Config.NAME);     // compile-time constant: inlined, no init!
        System.out.println(Config.port);     // first active use → static block runs
        System.out.println(Config.port);     // already initialised
    }
}

class Config {
    static final String NAME = "app";       // constant — copied into the caller
    static int port = 8080;
    static { System.out.println("Config initialised"); }
}
Output
main starts
app
Config initialised
8080
8080

What triggers class initialisation?

What is the parent delegation model and why does it exist?

ClassNotFoundException vs NoClassDefFoundError?

Free for students and teachers

Write notes like this one

Cards for every idea, quizzes and flashcards, read aloud — on the web and your phone.

Something wrong with this note? Open it to report