Java Development Kit (JDK), Java Virtual Machine (JVM) und Java Runtime Environment (JRE) bilden ein potentes Dreiergespann, um Java-Anwendungen zu entwickeln und auszuführen. Zu verstehen, wie JRE, JVM und JDK zusammenwirken, ist hilfreich - insbesondere für die Arbeit in Cloud- und DevOps-Umgebungen. Hier spielt die Java-Laufzeitumgebung in Sachen Monitoring und Konfiguration eine noch stärkere Rolle als bei der traditionellen Java-Anwendungsentwicklung.
Die Java-Laufzeitumgebung
Praktisch gesehen ist eine Laufzeitumgebung ein Stück Software, das dazu dient, andere Software auszuführen. Als Laufzeitumgebung für java enthält Java Runtime Environment die Java-Klassenbibliotheken, den Java Class Loader und die Java Virtual Machine. In diesem System:
ist der Class Loader dafür verantwortlich, Classes korrekt zu laden, um sie mit den zentralen Java-Klassenbibliotheken zu verbinden.
stellt die JVM sicher, dass Java-Anwendungen über die nötigen Ressourcen verfügen, um auf Endgeräten oder in Cloud-Umgebungen zuverlässig und stabil zu laufen.
dient die JRE hauptsächlich als Container für die genannten Komponenten und zur Orchestrierung ihrer Aktivitäten.
JDK, JRE und JVM installieren |
Wenn Sie ein JDK herunterladen, enthält es eine versionskompatible JRE. Diese enthält wiederum eine Standard-JVM. Sie können die JRE auch getrennt vom JDK herunterladen. Zudem steht eine Vielzahl von JVMs zur Wahl. Die Standardeinstellungen sind für die meisten Implementierungen gut geeignet - insbesondere, wenn Sie gerade mit Java beginnen. |
In aller Regel dient das Betriebssystem als Laufzeitumgebung für Software. Im Fall von Java-Applikationen hat sich das mit der Java Runtime Environment geändert. Sie dient seither als eine Art Vermittler zwischen Betriebssystem und der eigentlichen Anwendung. Dazu lädt die JRE Class-Files und startet eine virtuelle Maschine (die JVM), die Zugriff auf Speicher und andere Systemressourcen - über viele Betriebssysteme hinweg und in einheitlicher Form - sicherstellt.
Software lässt sich als eine Reihe von Schichten betrachten, die über der Systemhardware liegen. Jede Schicht stellt Dienste bereit, die von den darüber liegenden Schichten genutzt (und benötigt) werden. Die Java-Laufzeitumgebung erzeugt eine Softwareschicht, die JVM, die auf dem Betriebssystem eines Computers läuft und zusätzliche Java-spezifische Dienste bereitstellt. Die folgende Abbildung veranschaulicht diese Anordnung:
Dabei sorgt die JRE für einen "sanften Übergang" zwischen den Betriebssystemen - und dafür, dass Java-Programme ohne Anpassung auf nahezu jedem Betriebssystem laufen. Die Java-Laufzeitumgebung bietet außerdem zusätzliche Dienste. Einer der wichtigsten ist die automatische Speicherverwaltung, die die Entwickler von lästigen, manuellen Tasks befreit.
Man könnte die JRE als eine Art Meta-OS für java bezeichnen. Sie liefert ein klassisches Beispiel für Abstraktion, indem sie das zugrundeliegende Betriebssystem zu einer konsistenten Plattform abstrahiert, um Java-Anwendungen auszuführen.
Wie JRE und JVM zusammenwirken
Eine virtuelle Java-Maschine ist ein laufendes Softwaresystem, das für die Ausführung Java-Programme verantwortlich ist. Die JRE ist die Softwarekomponente auf der Festplatte, die Ihren kompilierten Java-Code (der Code wird mit dem JDK kompiliert) mit den erforderlichen Bibliotheken kombiniert und die JVM startet, um ihn auszuführen.
Dabei enthält die Java-Laufzeitumgebung Bibliotheken und Software, die nötig sind, damit Ihre Java-Programme laufen. Ein Beispiel dafür ist der Java Class Loader: Er lädt kompilierten Java-Code in den Speicher und verbindet den Code mit den entsprechenden Java-Klassenbibliotheken (ein Prozess, der auch als Linking bezeichnet wird).
In der eben beschriebenen mehrschichtigen Ansicht wird die JVM von der JRE erstellt. Aus der Paketperspektive betrachtet, enthält die JRE die JVM, wie im folgenden Schaubild dargestellt:
Die JVM ist der aktive, laufende Teil der JRE, der Programme hostet. Die JRE nimmt statische Assets und verwandelt sie in eine laufende JVM, die das laufende Programm hostet.
Java Runtime Environment installieren
Die Java-Laufzeitumgebung hat wie gesehen eine konzeptionelle Seite. In der Praxis handelt es sich jedoch auch nur um eine Software, die Sie auf Ihrem Rechner installieren.
Als Entwickler werden Sie hauptsächlich mit dem JDK und der JVM arbeiten, da diese Plattformkomponenten zum Einsatz kommen, um Java-Programme zu entwickeln und auszuführen. Als Benutzer einer Java-Anwendung haben Sie mehr mit der JRE zu tun, die es Ihnen ermöglicht, diese Programme auszuführen. Mit Java 9 wurde die Java-Plattform so umstrukturiert, dass die JRE nur noch im Rahmen eines JDK verfügbar ist. Wenn Sie eine Consumer-App im Bundle mit JRE ausliefern wollen, ist das mit JLink möglich.
Sie können das neueste JDK für Ihr System über Oracles Java SE-Website herunterladen. Für Windows und macOS gibt es automatische Installer. Unter Linux bietet sich die Verwendung von SDKMan an. In jedem Fall sollten Sie die JRE über die Befehlszeile verfügbar halten, um den java
-Befehl verwenden zu können.
JRE-Versionen
Die Java-Laufzeitumgebung wird mit jeder neuen Java-Version aktualisiert. Die Versionsnummern richten sich dabei nach dem Versionssystem der Java-Plattform. JRE 1.19 führt beispielsweise Java 19 aus.
Auf vielen Computern läuft eine für Java SE entwickelte JRE, mit der jede Java-Anwendung ausgeführt werden kann, unabhängig davon, wie sie entwickelt wurde. Die meisten mobilen Geräte werden mit einer JRE für Java ME geliefert, die vorinstalliert ist und nicht heruntergeladen werden kann. In Zukunft dürften Anwendungen, die über JLink mit einer eigenen JRE gebündelt sind, zur Norm werden.
Sobald Sie das JDK heruntergeladen haben, können Sie mit der enthaltenen JRE über die Kommandozeile interagieren. Der Befehl java -version
gibt dabei Auskunft darüber, welche Version installiert ist. Auf POSIX-Systemen können Sie das mit dem Befehl which java
überprüfen.
Java-Laufzeitumgebung meets DevOps
Die JRE bleibt in der Entwicklungsphase eher unauffällig, da sie meist nur dazu dient, Ihre Programme im Betriebssystem oder der IDE Ihrer Wahl auszuführen. In der Entwicklungs- und Systemadministration spielt sie hingegen eine größere Rolle, da sie für Monitoring und Konfiguration eingesetzt wird.
Dabei stellt die Java Runtime Environment im Grunde die "Knöpfe" zur Verfügung, um die Eigenschaften einer Java-Anwendung zu konfigurieren und zu steuern. Die Speichernutzung ist an dieser Stelle ein Paradebeispiel: Sie ist zwar immer wichtig, aber bei Cloud-Konfigurationen von entscheidender Bedeutung. DevOps ist ein Cloud-basierter Ansatz ist, um Software zu erstellen und auszuführen. Wenn Sie in diesem Bereich arbeiten (oder einsteigen wollen), sollten Sie verstehen, wie Java-Speicher funktioniert und wie er in der JRE überwacht wird.
DevOps oder Sysadmin? |
DevOps ist ein relativ neuer Begriff, der etwas beschreibt, was schon seit Jahrzehnten Bestand hat: Die Interoperabilität zwischen Entwicklung und Betrieb. In diesem Sinne ist DevOps nur eine aktueller Umschreibung für das, was früher als Operations oder Systemadministration bezeichnet wurde. Wie bei der Systemadministration besteht auch bei DevOps ein wichtiger Aspekt darin, Systeme zu managen, die für die Ausführung von Software erforderlich sind. Dazu gehört auch, die JRE als Teil von Systemen, auf denen Java-Anwendungen laufen, zu managen. |
Java Memory und die JRE
Java Memory besteht im Allgemeinen aus drei Komponenten:
Im Metaspace speichert Java die unveränderlichen Informationen des Programms - zum Beispiel Klassendefinitionen.
Im Heap Space speichert Java den Inhalt von Variablen.
Im Stack Space speichert Java die Ausführung von Funktionen und Variablenreferenzen.
Java Memory Management seit Java 8
Bis Java 8 war der Metaspace als "Permgen" bekannt. Metaspace ist nicht nur ein viel coolerer Name, sondern markiert auch eine bedeutende Veränderung bezüglich der Art und Weise, wie Entwickler mit dem Speicherbereich von Java interagieren. Zuvor mussten Sie die Größe des permgen-Speicherplatzes mit dem Befehl java -XX:MaxPermSize
überwachen. Seit Java 8 wird der Metaspace automatisch vergrößert - entsprechend dem Bedarf des Programms. Mit Java 8 hält auch ein neues Flag, MaxMetaspaceSize
, Einzug, mit dem Sie die Größe des Metaspace begrenzen können.
Heap Space konfigurieren
Der Heap Space ist der dynamischste Teil des Java-Speichersystems. Sie können die Flags -Xms
und -Xmx
verwenden, um die Größe des Heap Space initial zu definieren und eine Kapazitätsgrenze für die Zukunft zu setzen. Zu verstehen, wie sich diese Flags auf spezifische Programmanforderungen abstimmen lassen, ist ein entscheidender Aspekt beim Java Memory Management. Idealerweise ist der Heap Space groß genug, um eine möglichst gute Garbage Collection zu erreichen. Im Klartext: Nicht mehr Speicher als nötig.
Stack Space konfigurieren
Im Stack Space werden Funktionsaufrufe und Variablenreferenzen in eine Warteschlange gestellt. Der Stack Space ist zudem die Quelle für die zweitberüchtigtste Ausnahme in der Java-Programmierung: dem Stack Overflow Error (die Nummer Eins ist die Null Pointer Exception). Sie zeigt an, dass der Stack Space voll ist, weil zu viel Speicherplatz reserviert wurde. Zu einem Stack Overflow kommt es für gewöhnlich, wenn eine oder mehrere Methoden sich gegenseitig zirkulär aufrufen und dadurch eine ständig wachsende Zahl von Funktionsaufrufen in den Stack einfließen.
Mit dem -Xss
-Switch können Sie die Startgröße des Stacks konfigurieren. Der Stack wächst dann enstprechend den Programmanforderungen dynamisch.
Java Application Monitoring mit JRE
Zwar ist Application Monitoring eine JVM-Funktion, allerdings bietet die Java-Laufzeitumgebung Konfigurationsoptionen, die die Grundlage dafür schaffen. Um Java-Anwendungen zu überwachen, steht eine Vielzahl von Tools zur Verfügung - von Klassikern (wie dem Unic-Befehl top
) bis hin zu ausgefeilten Remote-Monitoring-Lösungen wie Oracle Infrastructure Monitoring. (fm)
Dieser Beitrag basiert auf einem Artikel unserer US-Schwesterpublikation Infoworld.