skip to content

Class Loading & Initialization

How a type gets from raw bytes to a usable runtime class: loading, verification, preparation, resolution, initialization, and the loader hierarchy that gives each type its identity. Interviewers reach for it when they want depth, since NoClassDefFoundError and "X cannot be cast to X" only make sense once you know this.

on this pageshow

explore

questions

page 1 of 2

Which class loaders does a modern JVM create at startup, what does each of them load, and how are they linked to one another?

level: juniorimportance: must knowfreq 60%

answer

  1. Three built-ins: application -> platform -> bootstrap
  2. Bootstrap is native, no parent, represented by null
  3. Platform = remaining JDK modules
  4. Application = class path + application modules
  5. Parent is a reference, not inheritance

basics

~20 s

Three built-in loaders: the bootstrap loader (native, no parent) defines the core runtime classes; the platform loader defines the remaining JDK modules; the application (system) loader defines your code from the class path and module path. They form a chain: application -> platform -> bootstrap.

solid answer

~50 s

A running JVM starts with three built-in loaders arranged in a chain. **Bootstrap loader**: implemented inside the VM in native code, so it has no Java object representing it and no parent. It defines the core runtime classes, `java.lang`, `java.util` and the rest of `java.base`, straight from the runtime image. **Platform loader**: defines the remaining JDK platform modules that are not part of the core set, for example `java.sql` or `java.xml.crypto`. Its parent is the bootstrap loader. **Application (system) loader**: defines application classes from the class path and from application modules on the module path. Its parent is the platform loader; it is what `ClassLoader.getSystemClassLoader()` returns and what `getClassLoader()` reports for your own classes. The chain is a *parent* relationship, not inheritance: each loader holds a reference to its parent and consults it before defining a class itself. Each loader is also a namespace: a type's identity is the pair of its name and its defining loader, so which loader defined a class matters, not just the name.

code

java · 10 lines
java
System.out.println(String.class.getClassLoader());
// null  -> bootstrap loader (native, no Java object)

ClassLoader app = MyApp.class.getClassLoader();
System.out.println(app);            // jdk.internal.loader.ClassLoaders$AppClassLoader@...
System.out.println(app.getParent());// jdk.internal.loader.ClassLoaders$PlatformClassLoader@...
System.out.println(app.getParent().getParent()); // null -> bootstrap

System.out.println(ClassLoader.getSystemClassLoader() == app);   // true
System.out.println(ClassLoader.getPlatformClassLoader() == app.getParent()); // true

go deeper

for a junior

Name the three loaders, what each loads, and the order of the chain. Knowing that bootstrap shows up as null is the detail interviewers check.

for a middle

Add that parent is a reference rather than inheritance, that the bootstrap loader is native with no Java object, and that each loader is a namespace so identity is name plus defining loader.

for a senior

Connect the roles to guarantees: an unsubstitutable core, and namespaces that let one process host conflicting library versions with framework loaders extending the tree downward.

for a principal

Discuss the arrangement as an isolation mechanism, what the platform anchors, where extension points belong in the tree, and the consequences of choosing where a shared type is defined.

## The startup trio When a JVM starts it creates a small, fixed set of loaders. They are built in, they always exist, and every class in the process is ultimately defined by one of them or by a loader someone created underneath them. **The bootstrap loader** is special because it lives inside the virtual machine itself, written in native code as part of the runtime rather than as a Java class. It defines the classes the runtime cannot function without: `java.lang.Object`, `java.lang.String`, `java.lang.Class`, the collections, and generally the contents of the core module `java.base`. Because it is native, there is no `ClassLoader` instance for it, and it has no parent, it is the root of the chain. It reads its classes from the runtime image shipped with the JDK, not from a JAR you can point at. **The platform loader** sits directly beneath it. Its job is the rest of the platform: JDK modules that are part of the specification or the JDK distribution but not the core set, such as database, XML and cryptography modules. Practically, it exists so the platform can be partitioned, some modules are always present and privileged, others are ordinary platform code. Its parent is the bootstrap loader, and it is reachable from Java code via `ClassLoader.getPlatformClassLoader()`. **The application loader**, also called the system loader, is the one your code meets. It defines classes from the **class path** and from application **modules on the module path**. Its parent is the platform loader. `ClassLoader.getSystemClassLoader()` returns it, and calling `getClassLoader()` on one of your own classes returns this object (unless a framework created a loader beneath it). ## What 'parent' actually means This is the most common misconception worth nailing down: the chain is **not** class inheritance. Each loader simply holds a reference to another loader designated as its parent. `ClassLoader` is an ordinary class; the built-in loaders are instances (except bootstrap, which has none). The chain is a runtime data structure, walkable with `getParent()` until you reach `null`, which represents the bootstrap loader. So the practical shape is: ``` application (system) loader | parent platform loader | parent bootstrap loader (represented by null) ``` Application frameworks and containers add loaders *below* the application loader, typically one per plugin, web application or deployment unit, extending the tree downward. ## Why the split into roles matters Separating the loaders is not bookkeeping; it enforces two properties. First, **trust and integrity of the core**. Because the core classes are defined by a loader nobody can substitute, application code cannot supply its own `java.lang.String` and have it used by the runtime. The core is anchored. Second, **namespaces**. A loaded type's runtime identity is the pair *(binary name, defining loader)*, not the name alone. Two loaders can each define a class called `com.acme.Widget`, and the JVM treats those as two unrelated types. This is what makes containers able to host applications with different versions of the same library in one process, and it is why the loader that defined a class is a meaningful property rather than a trivia detail. It also means the loader that *initiates* a request and the loader that ultimately *defines* the class need not be the same: `getClassLoader()` reports the defining one. ## Reading the chain in practice A few observations that come up constantly: - `String.class.getClassLoader()` returns `null`, because the bootstrap loader has no Java representation. `null` in loader APIs conventionally means bootstrap. - Your class returns the application loader; a JDK class such as one from the SQL module returns the platform loader. - Walking `getParent()` from the application loader gives platform, then `null`. - The built-in loaders in current JVMs are internal implementation types, not the general-purpose URL-based loader class some older code assumes. ## Where classes actually come from The bootstrap and platform loaders read from the runtime image built into the JDK. The application loader reads the class path (directories and JARs, in order) and the module path (application modules). Nothing in the chain is inherently tied to files: a loader is defined by its ability to produce bytes for a name, which is exactly why custom loaders can fetch from anywhere. ## The short version to say out loud Three built-in loaders, chained child-to-parent: application, platform, bootstrap. Bootstrap is native, has no parent and no Java object, and owns the core runtime. Platform owns the remaining JDK modules. Application owns your code. Each is a namespace boundary, so a class's identity includes which loader defined it.

  • Is the parent relationship between class loaders the same as class inheritance?
    No. Each loader stores a reference to another loader as its parent, and you follow it with getParent(); it is a runtime link between instances. The built-in loaders are not subclasses of one another in any meaningful sense, and a custom loader picks its parent by passing it to the ClassLoader constructor rather than by extending anything.
  • If two class loaders both define a class named com.acme.Widget, does the JVM see one type or two?
    Two. A loaded type's identity is the pair of its binary name and its defining loader, so the two definitions are unrelated types even though the names match. Passing an instance across the boundary produces a ClassCastException whose message confusingly shows the same class name twice.

saying these in an interview costs you the question

  • Describing the loader chain as class inheritance rather than a parent reference
  • Claiming the bootstrap loader is a ClassLoader object you can obtain
  • Saying the platform loader loads java.lang and the core classes
  • Believing a class's identity is just its fully qualified name, independent of the loader
  • Assuming custom loaders sit above the application loader rather than below it

context

open as a page

When running code references a class for the first time, the JVM has to load it. Concretely, what happens during that loading step — where do the class's bytes come from, and what does the JVM end up with in memory?

level: juniorimportance: must knowfreq 60%

basics

~20 s

A class loader is asked for a binary name and produces the class's bytes (usually a .class file on the classpath, but any source works). The JVM parses that byte stream, loads the superclass and superinterfaces first, and creates a runtime type: internal class metadata plus a java.lang.Class object on the heap.

open as a page

Walk through, step by step, what a standard Java class loader does when it is asked to load a type it has not loaded before. Which loader ends up actually defining a core JDK type such as java.util.List?

level: juniorimportance: must knowfreq 60%

basics

~20 s

It first checks whether it has already loaded that name; if not, it asks its parent, which asks its own parent, all the way up to the bootstrap loader. Only if every ancestor fails does it try to find the bytes itself. Core JDK types are therefore always defined by the bootstrap loader.

open as a page

Inside one running JVM, is a fully-qualified name such as com.acme.Config enough to identify a type uniquely? Explain what actually determines a class's runtime identity.

level: middleimportance: must knowfreq 45%

basics

~20 s

No. A runtime type is identified by the pair (fully-qualified name, defining class loader). Load the same class file with two different loaders and you get two distinct, non-interchangeable types, each with its own Class object.

open as a page

How would you implement a class loader that loads class bytes from a non-standard source such as a database row or an encrypted archive, and which methods of java.lang.ClassLoader do you override?

level: middleimportance: must knowfreq 45%

basics

~20 s

Extend java.lang.ClassLoader, override findClass(name): fetch the bytes yourself, then call the protected defineClass(name, bytes, 0, len), which asks the VM to parse them into a runtime Class. Leave loadClass alone so parent delegation still works.

open as a page

After the JVM has read a class file into the runtime, linking happens in three sub-phases before any of the class's own initializer code runs. Name them and describe what each one is responsible for.

level: middleimportance: must knowfreq 48%

basics

~20 s

Verification checks that the bytecode is structurally and type-safe and that the operand stack is disciplined. Preparation allocates the static fields and sets them to default zero values, not to their programmed initializers. Resolution replaces symbolic references in the constant pool with direct references, and may be done lazily.

open as a page

In the JVM, what is the difference between a ClassNotFoundException and a NoClassDefFoundError, and what does each one tell you about how the class was being loaded?

level: middleimportance: must knowfreq 70%

basics

~20 s

ClassNotFoundException is a checked exception from explicit dynamic loading (Class.forName, loadClass) when the bytes cannot be found. NoClassDefFoundError is an Error: a type the compiler linked against symbolically is absent at runtime, or its initialization already failed.

open as a page

Why does the JVM have each class loader consult its parent before searching its own sources, rather than letting each one search locally first? Concretely, what would go wrong if a loader searched locally first — for instance if an application JAR contained a class named java.lang.String?

level: middleimportance: must knowfreq 55%

basics

~20 s

Two reasons: consistency and safety. Delegating upward guarantees every core type is defined exactly once, by the highest loader that has it, so all code agrees on the same type. It also stops application code substituting its own version of a trusted core class. The JVM additionally refuses outright to define classes in java.* from a non-bootstrap loader.

open as a page

An application throws `java.lang.ClassCastException: com.acme.Config cannot be cast to com.acme.Config` — the identical fully-qualified name on both sides. How is that possible, and how would you confirm the cause on a running system?

level: seniorimportance: must knowfreq 42%

basics

~20 s

The two names refer to two different run-time types, because the class was defined by two different class loaders. Confirm it by printing the class loader and identity hash of each side's Class object, and by finding the duplicate class source with -verbose:class.

open as a page

A Java service compiles cleanly but at runtime fails with NoSuchMethodError or AbstractMethodError against a third-party library. What family of JVM failures is this, and what actually causes it?

level: seniorimportance: must knowfreq 55%

basics

~20 s

It is the IncompatibleClassChangeError family under LinkageError: the runtime class no longer matches what the code was compiled against. Resolution is lazy and checked against the JAR actually on the classpath, so a version or duplicate-artifact mismatch shows up only when the member is first used.

open as a page

If the same class file is defined by two different class loaders inside one JVM, what happens to that class's static fields, its Class object, and any singleton instance it holds?

level: middleimportance: should knowfreq 34%

basics

~20 s

Everything per-type is duplicated: two Class objects, two independent sets of static fields, two runs of the static initializer, and therefore two singletons. Neither side can see or affect the other's state, and objects from one are not instances of the other.

open as a page

The Java compiler emits a synthetic method named `<clinit>` into a class file. What source constructs end up inside it and in what order, where does executing it sit relative to the JVM's loading, linking (verification, preparation, resolution) and initialization phases, and why does the JVM track "is initialized" per class-plus-defining-class-loader rather than per class name?

level: middleimportance: should knowfreq 42%

basics

~20 s

The compiler merges all static blocks and static field initializers, in source order, into one synthetic <clinit> method. It runs in the initialization phase, after linking's preparation already zeroed those statics. A class's runtime identity is its name plus its defining loader, so each loader gets its own statics and its own <clinit> run.

open as a page

In the JVM, when exactly does a Java `static int counter = 5;` field actually hold the value 5 — and how is `static final int LIMIT = 100;` treated differently by the class file and the runtime?

level: middleimportance: should knowfreq 40%

basics

~20 s

During preparation the field is allocated and set to 0; the value 5 is only written when the class is initialized and its static initializer runs. A compile-time constant like static final int LIMIT = 100 carries a ConstantValue attribute and is set during preparation — and javac also copies the literal into every class that reads it.

open as a page

Why does calling getClassLoader() on java.lang.String's Class object return null, while calling it on one of your own application classes returns a real object, and what does that null mean for code that uses the returned loader?

level: middleimportance: should knowfreq 45%

basics

~20 s

The bootstrap loader is implemented in native code inside the VM and has no Java ClassLoader instance, so loader APIs represent it as null. Your classes are defined by the application loader, a real object. Code must treat null as 'bootstrap', not as 'no loader'.

open as a page

Beyond a missing class, what do the JVM errors VerifyError, ClassFormatError, UnsupportedClassVersionError and UnsatisfiedLinkError each signal, and at which point of loading a class does each one arise?

level: middleimportance: should knowfreq 40%

basics

~20 s

All are LinkageError subtypes. ClassFormatError: the bytes are malformed during parsing. UnsupportedClassVersionError: a well-formed file whose class-file version is newer than the JVM supports. VerifyError: bytecode fails the verifier during linking. UnsatisfiedLinkError: a native method or native library cannot be linked.

open as a page

A JVM class loader can define a class from bytes that never existed as a file on disk. Explain the mechanism that makes that possible, and give real examples of where class bytes come from besides the file system.

level: middleimportance: should knowfreq 40%

basics

~20 s

All loading funnels through ClassLoader.defineClass(name, byte[], off, len), which is the only way a byte array becomes a runtime type. The JVM never looks at the origin of those bytes, so they can come from a JAR, a network stream, a database, an encrypted blob, or a generator that builds them in memory.

open as a page

Loading a Java class produces two related in-memory structures: the class metadata the JVM keeps in its class-metadata area (Metaspace in HotSpot) and the `java.lang.Class` instance on the heap. What lives in each, and how are they connected?

level: middleimportance: should knowfreq 38%

basics

~20 s

Metadata lives in native memory (HotSpot Metaspace): runtime constant pool, field and method descriptors, method bytecode, inheritance links and dispatch tables. The heap holds a java.lang.Class mirror — the Java-visible handle used by reflection and .class literals, which in HotSpot also stores the class's static field values. The two reference each other.

open as a page

When writing a custom java.lang.ClassLoader subclass, why is overriding findClass(String) the recommended approach while overriding loadClass(String) is discouraged? What changes behaviourally if you override loadClass?

level: middleimportance: should knowfreq 45%

basics

~20 s

findClass is the hook the inherited delegation algorithm calls only after every ancestor has failed, so overriding it adds a new source of bytes while preserving parent-first order. loadClass is the delegation algorithm, so overriding it replaces the policy — usually turning the loader child-first, with duplicate definitions as the risk.

open as a page

How can a long-running JVM pick up a new version of an application class without restarting the process, using class loaders — and what are the limits of that technique?

level: seniorimportance: should knowfreq 35%

basics

~20 s

Create a fresh class loader for the new version, define the new bytes there, build a new object graph, and atomically swap the reference. The old loader, its classes and all their instances are then abandoned; they are collected only when nothing references them. Live objects are not upgraded — state must be migrated explicitly.

open as a page

The JVM guarantees a class's static initializer runs exactly once even when many threads touch the class at the same instant. How is that guarantee implemented, and what failure mode does the very same mechanism create in production?

level: seniorimportance: should knowfreq 34%

basics

~20 s

Each class has an initialization lock and a state (uninitialized, in progress by thread T, initialized, erroneous). One thread runs the initializer while others block; recursive entry by the same thread is allowed and proceeds. Two classes initializing each other from two threads therefore deadlock, and slow initializers stall every caller.

open as a page

A Java application starts and runs for minutes, then throws NoSuchMethodError deep in a rarely used branch. Explain, in terms of how the JVM turns constant-pool symbolic references into direct references, why the failure surfaced only then.

level: seniorimportance: should knowfreq 42%

basics

~20 s

Class files reference members symbolically — by name and descriptor in the constant pool. HotSpot resolves each reference lazily, on first execution of the instruction that uses it. The rare branch's method reference was never resolved before, so the mismatch between the compiled-against and loaded classes only surfaced when the branch finally ran.

open as a page

What does the JVM's bytecode verifier actually prove about a class file, and why does the JVM verify at all when the bytecode was produced by a Java compiler?

level: seniorimportance: should knowfreq 38%

basics

~20 s

The verifier proves structural and type safety: operand-stack depth and types agree on every path, jumps target real instructions, locals are used with consistent types, access rules hold, and objects are initialized before use. It verifies because the JVM must assume bytecode came from anywhere, not from javac.

open as a page

How did the JVM's built-in class loaders change when Java 9 introduced the module system, compared with Java 8, and what kinds of existing code commonly break as a result?

level: seniorimportance: should knowfreq 35%

basics

~20 s

Java 8's extension loader (lib/ext) was replaced by the platform loader, and the built-in loaders are no longer URLClassLoader instances. Casting the system loader to URLClassLoader or reflectively calling addURL fails, and code scanning rt.jar breaks because platform classes now come from the runtime image.

open as a page

A single JVM process hosts several deployed applications that each need a different version of the same library. How is the class loader hierarchy typically arranged to make that possible, and what constraints does that arrangement put on types shared between the host and the applications?

level: seniorimportance: should knowfreq 40%

basics

~20 s

The host gives each deployment its own loader as a sibling under a shared parent. Because a type's identity is its name plus defining loader, each application gets a private copy of the library. Any type crossing the boundary must be defined by the common ancestor, or the two sides see incompatible types.

open as a page

Does the JVM guarantee that a class is loaded only at the moment it is first used? What latitude does the specification give an implementation about *when* loading happens, and how is that latitude kept unobservable?

level: seniorimportance: should knowfreq 30%

basics

~20 s

No. An implementation may load classes eagerly, in bulk, or from a prebuilt archive. The only requirement is that any error from loading or linking is thrown at the point where the program would first actively use the class — so early loading must stay invisible. Initialization timing, by contrast, is strictly specified.

open as a page

A class that ships inside the JDK — say a factory that discovers implementations through java.util.ServiceLoader — needs to instantiate an implementation class that lives in your application's JAR. Strict parent-first delegation makes that impossible, because the JDK's loader cannot see application classes. How does the platform get around this?

level: seniorimportance: should knowfreq 45%

basics

~20 s

By deliberately inverting delegation: the JDK code obtains a loader that can see application classes and loads through it. Usually that is the thread context class loader, Thread.currentThread().getContextClassLoader(), which defaults to the application loader; APIs like ServiceLoader also accept an explicit loader argument.

open as a page

You are designing a plugin system where each plugin ships its own dependency jars and different plugins may require incompatible versions of the same library. How does giving each plugin its own class loader deliver that isolation, and what must remain shared for the host and plugins to communicate at all?

level: principalimportance: should knowfreq 33%

basics

~20 s

Because a type is (name + defining loader), each plugin loader defines its own private copy of its dependencies, so conflicting versions coexist without clashing. But every type crossing the boundary — the plugin API interfaces and any exchanged data types — must be defined once by a shared ancestor loader that all plugins delegate to.

open as a page

How would you keep a large multi-team JVM deployment from hitting runtime linkage failures such as NoSuchMethodError or NoClassDefFoundError, and what tradeoffs come with each control you would put in place?

level: principalimportance: should knowfreq 35%

basics

~20 s

Move the check earlier than execution: converge dependency versions centrally, fail builds on duplicate classes and convergence conflicts, gate published artifacts on binary compatibility, smoke-test the assembled runtime artifact, and isolate irreconcilable versions with shading or separate loaders.

open as a page

Servlet containers, application servers and plugin frameworks frequently invert the default order so that a component's own loader searches its bundled JARs before asking its parent. Why do they deliberately break parent-first delegation, and what problems does that decision create?

level: principalimportance: should knowfreq 33%

basics

~20 s

To isolate library versions: each component gets the exact dependency versions it bundles, rather than whatever the container happens to ship. The cost is duplicate class definitions — the same name defined by several loaders — producing cast failures at boundaries, duplicated static state, and memory retained per component.

open as a page

Two classes both declare the package com.acme.util, but they are defined by different class loaders. Can one access the other's package-private members? Explain what the JVM treats as 'the same package' at run time.

level: seniorimportance: nice to knowfreq 18%

basics

~20 s

No. The JVM's run-time package is the pair (package name, defining class loader), so same-named packages in different loaders are different packages. Package-private access across them fails with IllegalAccessError, even though the source-level package names match.

open as a page

showing 1–30 of 33