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.
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:
- Find — The loader locates the bytecode for the named class (from a JAR, the disk, the network, or generated on-the-fly)
- Verify and link — The JVM checks the bytecode is valid and resolves references to other classes
- 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:
| Loader | Loads | Purpose |
|---|---|---|
| Bootstrap | Core Java runtime classes | Ensures core types like java.lang.Object are always the same |
| Platform | Standard library modules outside the core | Java modules and extensions |
| Application | Your program's own classes | The 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
On first use, not at startup. The first time your code touches a type, the JVM asks a classloader to find and define it. This is called lazy loading, and it's why unused code never enters memory.
A classloader asks its parent to load a class before trying itself. The parent chain runs up to the bootstrap loader. This prevents the same class from being loaded twice under the same name by different loaders.
Yes, if different classloaders define them. The JVM identifies a class by both its name and its defining loader, so same-named classes from separate loaders are treated as distinct types.
To intercept classes as they load and transform them in memory. A custom loader sits between the disk and the JVM, modifying bytecode before the JVM defines the class. This is how mods patch Minecraft without modifying the game jar.