RizTech Academy logo
RizTech Academy
Getting StartedLesson 2 of 415 min

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:

  1. Upgrade the runtime. Usually correct, and usually the thing somebody forgot.
  2. Compile for the older version. javac --release 17 Item.java produces class file 61, and — importantly — also stops you calling library methods that only exist in 21. This is the right flag.
  3. Do not use -source and -target alone. 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 throws NoSuchMethodError at runtime on the old one. --release covers 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.

  1. Compile and disassemble. Save the Greeting class above, run javac Greeting.java, then javap -c Greeting. Find the iadd. Then change a + b to a * b, recompile, and diff the output. One instruction changes.

  2. Break it on purpose. Run javac --release 8 Greeting.java, then javap -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.

  3. Try jshell. Type jshell, then "12".length(), then Math.max(3, 9), then 2 + 2 * 3. Note that it prints the result without System.out.println. Type /exit to leave. You will use this whenever you are unsure what a method returns.

  4. Read a real error, deliberately. If you have two JDKs available, compile with the newer and run with the older, and read the UnsupportedClassVersionError yourself. 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 8 and 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