Learn · concepts

What Is a Java Classloader?

A classloader finds and loads Java classes into the JVM at runtime. Here's how they work, why the hierarchy matters, and why mods depend on them.

TRtrolPublished 5 min read

A classloader is the part of the Java runtime that finds a class file and loads it into the JVM. Every time your code touches a type for the first time, the JVM asks a classloader: "Find this class for me." The loader locates the bytecode, reads it, and the JVM defines the class in memory. This mechanism is invisible in normal Java programs but is the exact hook that mod frameworks use to patch Minecraft.

What a classloader actually does

A classloader turns a class name into a usable class inside the running JVM. You ask for a type, the loader finds the matching .class file (or generates it), verifies and links it, and the class is ready to use.

The work happens in three steps:

  1. Find — The loader locates the bytecode for the named class (from a JAR, the disk, the network, or generated on-the-fly)
  2. Verify and link — The JVM checks the bytecode is valid and resolves references to other classes
  3. Use — The class is ready and your program can instantiate it

This is why Java programs can dynamically load code at runtime that was never mentioned at compile time. The class name is enough for the loader to go find it.

The classloader hierarchy

Classloaders are arranged in a parent chain, and most of them ask their parent first. A standard Java application has three built-in classloaders:

LoaderLoadsPurpose
BootstrapCore Java runtime classesEnsures core types like java.lang.Object are always the same
PlatformStandard library modules outside the coreJava modules and extensions
ApplicationYour program's own classesThe classpath your program specifies

When the application loader is asked for a class, it normally delegates upward first. It asks the platform loader, which asks the bootstrap loader, which tries to load it. Only if no parent can load it does the child try. This is the delegation model, and it prevents the same class from being loaded twice under different names.

Class identity and how it works

A class is identified by both its name and the loader that defined it. Two different loaders can each load a class called com.example.MyClass, and the JVM treats them as two completely different types. This sounds obscure, but it's the foundation of everything interesting that Java can do.

Bootstrap Loader: java.lang.Object (the core Object)
Application Loader: java.lang.Object (if somehow re-loaded by app loader)
→ Two different types, same name
→ This breaks your program, which is why delegation prevents it

Custom classloaders

Programs can define their own classloaders. A custom loader can read bytecode from anywhere: a JAR file, the network, a database, or bytecode it generated or transformed on the fly. This is how:

  • Plugin systems load code dynamically without knowing about it at compile time
  • App servers (Tomcat, JBoss) run multiple applications in the same JVM, each with its own classloader
  • Minecraft mods patch game classes as they load

Why this matters for Minecraft mods

Mods exist because of custom classloading. A mod loader for Minecraft starts the game through its own custom classloader so it can see and modify game classes as they load. That interception point is where the magic happens.

When a loader sits between the disk and the JVM, it can transform a class right before the JVM defines it:

Disk: obfuscated Minecraft class
    ↓
Mod Loader (custom classloader)
    ↓ [bytecode transformation happens here]
    ↓
JVM: defines the patched class
    ↓
Memory: game runs with mods active

A mod can rewrite a game method, inject new behavior, add fields, all without touching the game JAR on disk. The class the JVM ends up running is the one the mod framework modified. This is exactly how Fabric, Forge, and other mod loaders patch Minecraft.

Lazy loading

Classes are not all loaded at startup. The JVM loads each class the first time it's actually used. This is called lazy loading:

Program starts → JVM doesn't load String class yet
User calls System.out.println() → String is needed → Loader loads it

This means a classloader only needs to find and load classes your code actually touches. Unused classes never enter memory. This is why your Minecraft startup can take a while (mods add lots of classes) but not freeze forever.

FAQ