Together, the Java Development Kit (JDK), the Java Virtual Machine (JVM), and the Java Runtime Environment (JRE) form a powerful trifecta of Java platform components for developing and running Java applications. I've previously introduced the JDK and JVM, and in this quick tutorial you'll learn about the JRE, which is the runtime environment for Java.
Practically speaking, a runtime environment is a piece of software that is designed to run other software. As the runtime environment for Java, the JRE contains the Java class libraries, the Java class loader, and the Java Virtual Machine. In this system:
- The class loader is responsible for correctly loading classes and connecting them with the core Java class libraries.
- The JVM is responsible for ensuring Java applications have the resources they need to run and perform well in your device or cloud environment.
- The JRE is mainly a container for those other components, and is responsible for orchestrating their activities.
We'll dig a lot deeper into how these components work together in the sections that follow.
What is a runtime environment?
A software program needs to execute, and to do that it needs an environment to run in. The runtime environment loads class files and ensures there is access to memory and other system resources to run them. In the past, most software used the operating system (OS) as its runtime environment. The program ran inside whatever computer it was on, but relied on operating system settings for resource access. Resources in this case would be things like memory and program files and dependencies. The Java Runtime Environment changed all that, at least for Java programs.
The Java Runtime Environment
We can look at software as a series of layers that sit on top of the system hardware. Each layer provides services that will be used (and required) by the layers above it. The Java Runtime Environment is a software layer that runs on top of a computer's operating system, providing additional services specific to Java.
The JRE smoothes over the diversity of operating systems, ensuring that Java programs can run on virtually any OS without modification. It also provides value-added services above-and-beyond the operating system. Automatic memory management is one of the JRE's most important services, ensuring that programmers don't have to manually control the allocation and reallocation of memory.
In short, the JRE is a sort of meta-OS for Java programs. It's a classic example of abstraction, abstracting the underlying operating system into a consistent platform for running Java applications.
How the JRE works with the JVM
A Java Virtual Machine is a running software system responsible for executing live Java programs. The JRE is the on-disk system that takes your Java code, combines it with the necessary libraries, and starts the JVM to execute it.
The JRE contains libraries and software that your Java programs need to run. As an example, the Java class loader is part of the Java Runtime Environment. This important piece of software loads compiled Java code into memory and connects the code to the appropriate Java class libraries.
In the layered view I just described, the JVM is created by the JRE. From a package perspective, the JRE contains the JVM, as Figure 1 shows.
Installing and using the JRE
While there is a conceptual side to the JRE, in real-world practice it's just software installed on a computer, whose purpose is to run your Java programs. As a developer, you'll mostly work with the JDK and JVM, because those are the platform components you use to develop and run your Java programs. As a Java application user you would be more involved with the JRE, which lets you run those programs.
In most cases, your computer will come with Java installed, and the JRE will be included with that. If you do ever need to manually install or upgrade, you can download the current JRE version from Oracle.
Once the JRE is installed, you can interact with it on the command-line by type
java -version, where it will tell you what version is installed. On POSIX systems, you can always check the installed location with
The JRE in devops
The JRE is not very noticeable in the development stage, where it mostly just runs your programs in the OS or IDE of your choice. The JRE plays a slightly more prominent role devops and systems administration because it's used for monitoring and configuration.
Basically, the JRE provides the "knobs" you would use to configure and control the characteristics of a Java application. Memory usage is a prime example, the bread and butter of systems administration. While memory usage is always important, it's vital in cloud configurations, and devops is a cloud-based technology. If you're working in a devops environment, or interested in branching out into devops, it's a good idea to understand how Java memory works and how it's monitored in the JRE.
Java memory and the JRE
Java memory consists of three components: the heap, stack and metaspace (which was previously called permgen).
- Metaspace is where Java keeps your program's unchanging info like class definitions.
- Heap space is where Java keeps variable content.
- Stack space is where Java stores function execution and variable references.
Memory management in Java 8
Until Java 8, metaspace was known as permgen. Besides being a much cooler name, metaspace is a significant change to how developers interact with Java's memory space. Previously, you would use the command
java -XX:MaxPermSize to monitor the size of permgen space. From Java 8 forward, Java automatically increases the size of the metaspace to accomodate your program's meta-needs. Java 8 also introduced a new flag,
MaxMetaspaceSize, which can be used to limit the metaspace size.
The other memory options, heap and stack, remain the same in Java 8.
Configuring heap space
Heap space is the most dynamic part of the Java memory system. You can use the
-Xmx flags to tell Java how big to start the heap, and how big to allow it to become. Understanding how to tune these flags for specific program needs is an important aspect of memory management in Java. The ideal is to make the heap big enough to attain the most efficient garbage collection. That is, you want to allow enough memory to let the program run, but you do not want it to be any bigger than necessary.
Configuring stack space
Stack space is where function calls and variable references are queued. Stack space is the source of the second-most-notorious error in Java programming: the stack overflow exception (the first is the null pointer exception). The stack overflow exception indicates that you've run out of stack space because too much of it has been reserved. Usually, you'll get a stack overflow when a method or methods call each other in a circular fashion, thereby devoting an ever-growing number of function calls into the stack.
You use the
-Xss switch to configure the stack starting size. The stack then grows dynamically according to the program's needs.
Java application monitoring
Although application monitoring is a function of the JVM, the JRE provides configuration options, which are the necessary baseline for monitoring. A variety of tools are available for monitoring Java applications, from the classics (like the Unix command
top) to sophisticated remote monitoring solutions like Oracle's infrastructure monitoring.
In between these options are visual profilers like VisualVM that allow for inspecting a running JVM. These tools enable tracking down hotspots and memory leaks, as well as watching overall memory consumption in your system.
The Java Runtime Environment is the on-disk program that loads Java applications for the JVM to execute. A JRE is included by default when you download the Java Development Kit, and each JRE includes the core Java class libraries, a Java class loader, and a Java Virtual Machine. It's helpful to understand how the JVM, JDK and JRE interact, especially for working in cloud and devops environments. In these environments, the JRE takes a stronger role in monitoring and configuration than it would in traditional Java application development.