Java JRE

Su Suriya Ravichandran Updated 14 Nov 2026
6 min read ·Lesson 33 of 133

The JRE (Java Runtime Environment) is the part of Java needed to run a compiled Java program, as opposed to writing and compiling one. It consists of the Java Virtual Machine plus the standard class library and supporting files. Understanding the JRE explains what a program actually depends on when it runs, and why modern Java deployment no longer relies on a separately installed JRE.

What the JRE Contains

JRE = JVM + standard class library + runtime support files
ComponentRole
JVMLoads, verifies, and executes bytecode; manages memory and threads
Class libraryThe ready-made classes programs call: java.lang, java.util, java.io, java.net, java.time, and the rest
ConfigurationSecurity settings, trusted certificate authorities, time-zone data, character encodings
The java launcherThe command that starts a JVM with a program

What it does not contain: javac, jar, javadoc, jshell, and the other development tools. Those are only in the JDK.

┌─────────────────────────────── JDK ───────────────────────────────┐
│  javac  jar  javadoc  jshell  jlink  jcmd  ...                    │
│  ┌──────────────────────────── JRE ────────────────────────────┐  │
│  │  class library (java.base, java.sql, java.net.http, ...)    │  │
│  │  ┌───────────────────────── JVM ─────────────────────────┐  │  │
│  │  │ class loader · verifier · interpreter · JIT · GC      │  │  │
│  │  └───────────────────────────────────────────────────────┘  │  │
│  └─────────────────────────────────────────────────────────────┘  │
└───────────────────────────────────────────────────────────────────┘

Why the Class Library Matters at Runtime

A compiled .class file does not contain the code of the library classes it uses. It contains only references to them by name. Consider:

import java.time.LocalDate;

public class Today {
    public static void main(String[] args) {
        System.out.println(LocalDate.now());
    }
}

After compiling, Today.class is tiny. It records that it calls java.time.LocalDate.now() and java.io.PrintStream.println(Object), but the code for those methods lives in the runtime's class library. When the program runs, the JVM loads LocalDate and PrintStream from the runtime installed on that machine.

Two consequences:

  1. The runtime version must support every API the program uses. A program calling a method added in Java 21 fails on a Java 17 runtime with NoSuchMethodError, even if it was compiled successfully on a Java 21 JDK.
  2. Library bug fixes and security patches arrive without recompiling. Updating the runtime from 25.0.1 to 25.0.2 changes the library code every program uses, with no change to the programs themselves.

The Version Check at Startup

Every .class file records the version of the class-file format it uses. A runtime refuses to load classes newer than itself:

# compiled with JDK 25, run on a Java 17 runtime
java Today
Error: LinkageError occurred while loading main class Today
    java.lang.UnsupportedClassVersionError: Today has been compiled by a more recent
    version of the Java Runtime (class file version 69.0), this version of the
    Java Runtime only recognizes class file versions up to 61.0

The class-file version is the Java version plus 44: Java 17 is 61, Java 21 is 65, Java 25 is 69. The fix is to run on a newer runtime, or compile for the older one:

javac --release 17 Today.java

History: From Separate JRE to Bundled Runtime

Until Java 8, there were two downloads:

  • The JRE for end users, who installed it once system-wide so that any Java application or browser applet could run.
  • The JDK for developers.

This model had problems. Applications depended on whatever JRE version happened to be installed. Updating the shared JRE could break one application while fixing another. Browser applets and Java Web Start, the main reasons for a system-wide JRE, were deprecated and then removed.

Since Java 11, Oracle and most vendors no longer publish a standalone JRE. The modern practice is that each application ships with its own runtime:

ApproachHow
Custom runtime imagejlink builds a runtime containing only the modules the app needs
Native installerjpackage bundles the app and a jlink runtime into .msi, .pkg, .deb, or .rpm
Container imageA Docker image based on a JDK or JRE-style base image (for example, the eclipse-temurin:<version>-jre images)
Server with a JDKThe operations team installs a specific JDK version on the server, managed like any other dependency

Some vendors (Eclipse Temurin, Amazon Corretto, Azul Zulu) still publish JRE-only packages or container images for convenience, which are smaller because they omit the development tools. Functionally they are what this lesson calls the JRE.

jlink produces a runtime image that is effectively a custom JRE:

# 1. Find out which modules the app uses
jdeps --print-module-deps --ignore-missing-deps app.jar
# java.base,java.sql

# 2. Build a runtime with only those modules
jlink --add-modules java.base,java.sql \
      --output app-runtime \
      --strip-debug --no-man-pages --no-header-files --compress=zip-6

# 3. Run the app with it
app-runtime/bin/java -jar app.jar

app-runtime contains a bin/java launcher, the JVM, and only the listed modules. It has no javac. It can be copied to a machine with no Java installed and run there (on the same operating system and CPU architecture it was built for).

The --compress option names vary between JDK versions (older versions used numeric levels such as --compress=2); check jlink --help for your JDK.

Runtime Configuration and System Properties

The runtime exposes information about itself through system properties:

public class RuntimeInfo {
    public static void main(String[] args) {
        String[] keys = {
            "java.version", "java.vendor", "java.home",
            "os.name", "os.arch", "file.encoding", "user.timezone"
        };
        for (String k : keys) {
            System.out.printf("%-14s %s%n", k, System.getProperty(k));
        }
        Runtime rt = Runtime.getRuntime();
        System.out.println("CPUs:          " + rt.availableProcessors());
        System.out.println("Max heap (MB): " + rt.maxMemory() / (1024 * 1024));
    }
}

Example output:

java.version   25.0.1
java.vendor    Eclipse Adoptium
java.home      /usr/lib/jvm/temurin-25-jdk-amd64
os.name        Linux
os.arch        amd64
file.encoding  UTF-8
user.timezone  Asia/Kolkata
CPUs:          8
Max heap (MB): 3980

java.home is the root of the runtime running this program. Runtime.getRuntime() returns an object describing the running JVM; maxMemory() reports the largest heap the JVM will attempt to use, which by default depends on the machine's physical memory. Values like user.timezone and file.encoding affect program behavior and can differ between a developer's laptop and a production server, which is a common source of "works on my machine" problems.

Properties can be set when launching:

java -Duser.timezone=UTC -Dapp.env=production -jar app.jar

A program reads custom properties with System.getProperty("app.env"). This is one way to pass configuration into an application without changing its code.

JRE in Practice

  • On a developer machine: install a JDK. It includes everything the JRE has.
  • On a server running one application: install a specific JDK or JRE package version, or ship a jlink runtime with the app.
  • In containers: use a JRE-style base image for smaller images, or a JDK image if you need diagnostic tools like jcmd inside the container.
  • For desktop applications: use jpackage to bundle a runtime so users do not need to install Java at all.

The guiding principle is that an application's runtime version should be chosen deliberately and tested, not left to whatever happens to be installed on the machine.

0 Comments

Reviewed before they appear

No comments yet.

Java
Java Introduction Java Features Java Syntax Java Comments Java Statements Java Output Java Variables Java Data Types Java Type Casting Java Operators Java Strings Java Numbers Java Math Java Booleans Java User Input Java Conditions Java If Else Java Switch Java While Loop Java For Loop Java Break Java Continue Java Arrays Java Methods Java Parameters Java Return Values Java Scope Java Recursion Install Java Install JDK Java IDEs Java JDK Java JRE Java JVM Java Compiler Java File Compile Java Run Java JShell Basics Java Packages Java OOP Java Classes Java Objects Class Methods Constructors Constructor Overload this Keyword static Keyword final Keyword Encapsulation Inheritance Polymorphism Abstraction Access Modifiers super Keyword Method Overload Method Override Instanceof Operator Nested Classes Inner Classes Anonymous Classes Enums Records Array Basics Array Length Loop Arrays Multidimensional Arrays Array Copy Array Sorting Array Search ArrayList LinkedList HashSet TreeSet HashMap TreeMap Queue Interface Deque Interface Stack Class Iterator List Interface Set Interface Map Interface Collections Class Comparable Comparator Generics Basics Generic Classes Generic Methods Wildcards String Basics String Length String Methods String Concatenation String Compare String Search String Replace String Split String Format StringBuilder StringBuffer Text Blocks Regular Expressions Number Types Integer Class Float Class Double Class BigInteger BigDecimal Math Class Random Numbers Number Format Date Basics LocalDate LocalTime LocalDateTime ZonedDateTime Date Formatting Date Parsing Period Class Duration Class Java Instant Class Java Exception Basics Java try...catch Java Multiple Catch Blocks Java finally Block Java throw Keyword Java throws Keyword Java Checked Exceptions Java Unchecked Exceptions Java Custom Exceptions Java try-with-resources Java Exception Chaining
Ask about this post
AI Ask about this post

Ask questions about Java JRE and get answers drawn from it.

Signed-in readers only.