JDK, JRE and JVM explained clearly
Three acronyms, and people get them wrong constantly — in blog posts, in college notes, and in interviews, where this is one of the commonest opening questions precisely because it is easy to answer badly.
It is also not trivia. Two of the most confusing errors a new Java developer meets come directly from not understanding this, and one of them is waiting for you at the end of this lesson.
What actually happens when you run a Java program
Four steps, and only the first two involve you.
Greeting.java → javac → Greeting.class → java → your program runs
(source, text) (bytecode) (the JVM)
You write Greeting.java. The compiler, javac, reads it, checks that every
type lines up, and writes Greeting.class. That file does not contain
instructions for your laptop's processor. It contains bytecode — instructions
for an abstract machine that does not physically exist.
Then the java command starts the JVM, which reads the bytecode and executes
it on your actual hardware.
You can look at bytecode. Compile this:
public class Greeting {
public static void main(String[] args) {
int a = 2;
int b = 3;
System.out.println(a + b);
}
}
Then run javap -c Greeting and you get:
public static void main(java.lang.String[]);
Code:
0: iconst_2
1: istore_1
2: iconst_3
3: istore_2
4: getstatic #7 // Field java/lang/System.out:Ljava/io/PrintStream;
7: iload_1
8: iload_2
9: iadd
10: invokevirtual #13 // Method java/io/PrintStream.println:(I)V
13: return
Read it once and move on — you will never write this. But notice what it shows:
iconst_2 pushes 2, istore_1 stores it in slot 1, iadd adds the top two
values. It is a small stack machine, and it is the same on every platform.
Two things follow from that, and both matter.
Your compiled code is portable. The same .class file runs on your Windows
laptop, a colleague's Mac and a Linux server, because each has its own JVM that
knows how to turn that bytecode into its own machine code. This is what "write
once, run anywhere" meant. It is broadly true, and the exceptions are real: file
paths, line endings, default character encodings and anything touching the
operating system still differ.
Your compiled code is not secret. javap is in every JDK, and proper
decompilers reconstruct readable Java from a .class file in seconds. Never put
a password, an API key or a database URL in Java source. Shipping a jar is
publishing your logic.
JVM, JRE, JDK
Three nested things.
JVM — Java Virtual Machine. The program that executes bytecode. It is platform-specific: there is a JVM built for Windows on x86, one for macOS on Apple silicon, one for Linux on ARM. It also does the work you never think about — allocating memory, and reclaiming it.
JRE — Java Runtime Environment. The JVM plus the standard library: String,
List, Math, file handling, networking. Enough to run a Java program, not to
compile one.
JDK — Java Development Kit. The JRE plus the tools you need to build:
javac, and a dozen others.
┌─ JDK ─────────────────────────────────┐
│ javac, jar, javadoc, jshell, javap… │
│ ┌─ JRE ───────────────────────────┐ │
│ │ standard library (java.*) │ │
│ │ ┌─ JVM ─────────────────────┐ │ │
│ │ │ executes bytecode │ │ │
│ │ └───────────────────────────┘ │ │
│ └─────────────────────────────────┘ │
└───────────────────────────────────────┘
You install a JDK. That is the whole practical answer. The JDK contains the JRE, which contains the JVM, so one download gives you all three.
One correction to most tutorials while you are here: there is no separate JRE
download any more. Until Java 8, Oracle shipped a small JRE for users who only
needed to run programs. From Java 9 onwards that stopped. If a page tells you to
"download the JRE", it was written before 2017. Applications that need to bundle
a runtime now build a trimmed one with jlink.
The tools in the JDK worth knowing
You will use the first three constantly, the rest occasionally, and knowing they exist is the point.
| Tool | What it does |
|---|---|
javac |
Compiles .java to .class |
java |
Starts the JVM and runs a class, a jar, or a single source file |
jshell |
An interactive Java prompt — try an expression without a file |
jar |
Packs classes into a single .jar archive |
javap |
Shows a class's structure or bytecode, as above |
javadoc |
Generates HTML documentation from /// comments |
jlink |
Builds a minimal runtime containing only the modules you use |
jpackage |
Wraps an application into a platform installer |
jcmd |
Asks a running JVM what it is doing — threads, memory, more |
jdb |
Command-line debugger. You will use the IDE's instead. |
jshell is worth trying on day one. It is the closest Java has to Python's REPL,
and for "does "12".length() return what I think" it beats writing a file.
The interesting part: bytecode is not the end
The JVM does not plod through bytecode one instruction at a time forever. It starts by interpreting, watches which methods run often, and then the JIT compiler — just-in-time — translates those hot methods into real machine code, optimising them with knowledge an ahead-of-time compiler never has: which branch actually gets taken, which type actually turns up.
This is why a Java service is slow for its first few seconds and then fast, and why benchmarking Java badly is so easy — measure the first thousand iterations and you have measured the interpreter.
It is also why "Java is slow" is a 1998 opinion. A long-running Java server is usually within a small factor of C, and comfortably faster than Python, precisely because the JIT had real data to optimise against.
The other job the JVM does for you is garbage collection. You never free memory in Java. Objects you can no longer reach get reclaimed automatically. The cost is that collection takes time, occasionally at a moment you did not choose — which is why Java is not used for pacemakers and is entirely fine for a payments backend.
The error this lesson exists to explain
Now the bug. Compile a class with Java 21 and try to run it on a Java 17 runtime:
Error: LinkageError occurred while loading main class Item
java.lang.UnsupportedClassVersionError: Item has been compiled by a more
recent version of the Java Runtime (class file version 65.0), this version
of the Java Runtime only recognizes class file versions up to 61.0
That is a real message, not a paraphrase, and it is one of the most common reasons a build that works on a developer's laptop fails on a server.
Every .class file records the version it was compiled for. A JVM will run
bytecode from its own version or older, and refuses anything newer — it cannot,
because a new release can add instructions the old JVM has never heard of.
Decoding the numbers: major version = Java version + 44.
| Java | Class file version |
|---|---|
| 8 | 52 |
| 11 | 55 |
| 17 | 61 |
| 21 | 65 |
| 25 | 69 |
So "65.0 but only up to 61.0" reads as "this was built for Java 21, I am a Java 17 runtime". Being able to decode that on sight is genuinely useful.
Three ways out, in order of preference:
- Upgrade the runtime. Usually correct, and usually the thing somebody forgot.
- Compile for the older version.
javac --release 17 Item.javaproduces class file 61, and — importantly — also stops you calling library methods that only exist in 21. This is the right flag. - Do not use
-sourceand-targetalone. They set the language level and the class file version but leave the compiler checking against your new standard library, so code compiles cleanly and then throwsNoSuchMethodErrorat runtime on the old one.--releasecovers all three and is the flag to remember.
The other error from this family is ClassNotFoundException /
NoClassDefFoundError: the JVM looked for a class on its classpath and did not
find it. That one comes back in the Maven module, where the classpath stops being
something you type by hand.
Check your work
What does javac produce, and is it machine code? A .class file containing
bytecode — instructions for the JVM, not for your processor. The JVM turns it
into machine code later, at runtime.
Which do you install? The JDK. It contains the JRE, which contains the JVM. There has been no separate JRE download since Java 9.
A server reports class file version 65.0 but "only recognizes up to 61.0".
What happened, and what is the cleanest fix? The class was compiled for Java
21 and the server runs Java 17. Cleanest fix is upgrading the server's runtime to
21; failing that, compile with --release 17, which also prevents you from
calling Java 21 library methods that would fail at runtime.
Why should a password never go in Java source? Because javap and
decompilers reconstruct readable code from a .class file. Shipping compiled
Java is publishing it.
Why is a Java program often slow for its first few seconds? The JVM interprets bytecode at first, and only after a method has run many times does the JIT compiler translate it into optimised machine code.
Practice
You need the JDK installed for these, so if you have not done the next lesson yet, come back — the first one is worth doing properly rather than reading.
-
Compile and disassemble. Save the
Greetingclass above, runjavac Greeting.java, thenjavap -c Greeting. Find theiadd. Then changea + btoa * b, recompile, and diff the output. One instruction changes. -
Break it on purpose. Run
javac --release 8 Greeting.java, thenjavap -verbose Greeting | grep major. Confirm you get 52, not the version for your JDK. This is what a build server does when it is configured for an older target. -
Try
jshell. Typejshell, then"12".length(), thenMath.max(3, 9), then2 + 2 * 3. Note that it prints the result withoutSystem.out.println. Type/exitto leave. You will use this whenever you are unsure what a method returns. -
Read a real error, deliberately. If you have two JDKs available, compile with the newer and run with the older, and read the
UnsupportedClassVersionErroryourself. Say out loud which number is your compiler and which is your runtime before you fix it. If you only have one JDK, compile with--release 8and confirm it still runs — a newer JVM running older bytecode is always fine. That asymmetry is the whole rule.
Next: installing Java 21 and IntelliJ IDEA, including the JAVA_HOME problem
that eats an afternoon if nobody warns you.
Stuck on this lesson?
Being stuck is part of it — but being stuck alone for three days is not. Our internship programme pairs this curriculum with code review and one-to-one help from working developers, and it is free.
About the internship