Learn · concepts
What Is JVM Bytecode? Java Class Files Explained
JVM bytecode is the compact instruction set the Java Virtual Machine runs. Learn how .class files work, why mods rewrite bytecode, and where ASM fits in.
What Is JVM Bytecode?
JVM bytecode is the instruction set the Java Virtual Machine executes. When you compile Java source, the compiler does not produce machine code for your CPU. It produces bytecode, a compact, portable format stored in .class files. The JVM reads those instructions at runtime and turns them into native code as it goes.
This layered approach—source to bytecode to machine code—is what makes Java portable. The same .class file runs on Windows, macOS, Linux, or any other system with a compatible JVM, because the JVM is the layer that knows how to execute the instructions on the local machine. Write once, run anywhere.
How source becomes bytecode
A .java file is text a person writes. A .class file is the compiled output the JVM runs. The compiler reads your source, checks it, and writes one .class file per class, each holding bytecode instructions plus a constant pool, method tables, and other metadata.
Bytecode is not tied to any one processor. The same .class file runs on Windows, macOS, Linux, or anything with a Java runtime, because the JVM is the layer that knows how to execute the instructions on the local machine. That portability is the whole point of the format.
What the instructions look like
Bytecode is a stack-based instruction set. Most operations push values onto an operand stack, pop them off, and push a result. There is no register allocation to reason about; the model is deliberately simple so the JVM can verify and execute it quickly.
A few example opcodes give the flavor:
| Opcode | What it does |
|---|---|
iload | Push an int local variable onto the stack |
iadd | Pop two ints, push their sum |
invokevirtual | Call an instance method |
getfield | Read an instance field |
return | Return from a method |
You rarely read or write these by hand. Tools generate and consume them, which is where a library like ASM comes in.
Why bytecode matters for mods
Bytecode matters because it lets you change behavior without the original source. A Minecraft mod almost never has the game's source code. What it has is the compiled game on disk. To add or alter a feature, a mod reads the existing bytecode, rewrites it, and feeds the result back to the JVM as the classes load.
This is how patching frameworks insert hooks into methods they do not own. They find the right spot in a compiled method and splice in a call to the mod's code. Working at the bytecode level means a mod can attach to the game even though it never sees a line of the original Java.
In practice, this is how Terminus and other Fabric-based clients modify Minecraft. Instead of having Minecraft's source, they work with the compiled .class files and rewrite the bytecode to add features like modules, settings screens, and custom rendering.
Where ASM fits
ASM is a Java library for reading, writing, and transforming bytecode. It parses a .class file into events or a tree, lets code modify the instructions, and emits a valid .class file back out. It is small, fast, and works close to the raw format, which is why higher-level modding tools build on top of it rather than parsing class files themselves.
Most modders never call ASM directly. They use a higher-level layer that hides the opcodes and exposes a friendlier way to say "run my code at the start of this method." Underneath, that layer is editing bytecode, often through ASM.
Decompiling bytecode back to source
Decompilers like Fernflower and CFR take compiled .class files and reconstruct readable Java from the bytecode. The process isn't perfect: some names and comments are lost, some constructs are harder to recover than others, and obfuscated code becomes unreadable. But for ordinary, unobfuscated code, decompilers usually produce output that's close enough to the original to understand and work with.
This is useful for learning how the game works, but it's not a substitute for having the actual source. The decompiled code is still compiled logic without comments, and reconstructed variable names can be misleading. It's a reading tool, not a writing one.
FAQ
No. Machine code is native instructions for a specific CPU. Bytecode is the JVM's own instruction set, which the JVM translates to machine code at runtime. That extra layer is what makes a .class file run on any platform.
Partly. Decompilers reconstruct readable Java from bytecode, and they get close for ordinary code. The result is not always identical to the original, and comments and some local names are gone, but it is usually enough to follow.
Because mods usually do not have the source. They ship against the compiled game. Rewriting bytecode lets a mod change behavior without ever touching, or even having, the original Java files.
Usually not. Modding frameworks and scripting layers handle the bytecode work for you. Knowing how it works helps when you debug a tricky patch or build tooling.