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. 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:
We'll dig a lot deeper into how these components work together in the sections that follow.
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.
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. 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.
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.
Figure 1. The JRE contains the JVM
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 entering java -version
, which will tell you what version is installed. On POSIX systems, you can always check the installed location with which java
.
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 consists of three components: the heap, stack and metaspace (which was previously called permgen).
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.
Heap space is the most dynamic part of the Java memory system. You can use the -Xms
and -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.
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.
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.