Weitere aktuelle Java-Titel finden Sie bei dpunkt.
 Inhaltsverzeichnis   Auf Ebene Zurück   Seite Zurück   Seite Vor   Auf Ebene Vor   Eine Ebene höher   Index


14.1

Die Runtime-Umgebung


Die im Paket java.lang definierte Klasse Runtime besitzt Methoden, durch die eine direkte Kommunikation mit dem System möglich ist. Dies beinhaltet unter anderem: Die Klasse Runtime besitzt die statische Methode getRuntime(). Diese Methode liefert als Ergebnis das Runtime-Exemplar der Virtual Machine:
  Runtime rt = Runtime.getRuntime();
Durch Aufruf der Methoden dieses Exemplars kann man aktuelle Parameter vom Betriebssystem bzw. von der Java Virtual Machine abfragen.

Die Abfrage des Speichers der Virtual Machine kann relativ einfach bewerkstelligt werden, sowohl in Applets als auch in Applikationen. Hierzu sind drei Methoden vorhanden: Da die Speicherallokation in der Virtual Machine stufenweise erfolgt, kann sich der Rückgabewert von totalMemory() während der Laufzeit einer Anwendung verändern. Beim Start reserviert die Virtual Machine beim Betriebssystem zunächst eine bestimmte Menge an Speicherplatz, der von der Anwendung verbraucht werden kann. Das heißt, der Speicherbedarf für neu erzeugte Objekte wird zunächst aus dem internen Vorrat gedeckt. Der aktuelle Gesamtvorrat an Speicher in der Virtual Machine wird von totalMemory() zurückgeliefert. freeMemory() gibt hingegen die Größe des noch nicht von der Anwendung belegten Speichers zurück, der jedoch bereits beim Betriebssystem von der Virtual Machine reserviert wurde. Wenn der initial reservierte Speicher für die auszuführende Anwendung nicht ausreicht, so fordert die Virtual Machine zusätzlichen Speicher an, der anschließend wieder intern für die Erzeugung von neuen Objekten benutzt werden kann.

Diese Speicherabfragemethoden können sehr gut zur Verfolgung des Speicherverbrauchs einer Anwendung benutzt werden: Entweder intern zu Debugging-Zwecken oder in Profiling-Tools, die den Speicherverbrauch einer Anwendung sichtbar machen, mit dem Ziel Code-Passagen mit zu hohem Speicherverbrauch aufzuspüren, um sie später durch effizienteren Code zu ersetzen.

Etwas schwieriger ist in Java der Umgang mit Prozessen. Die Ausführung eines externen Prozesses ist möglich, wenn der Anwendung entsprechende Rechte zur Verfügung stehen. So z. B. bei Applets, die lokal in den Appletviewer geladen werden (zum Begriff »lokal« siehe Kapitel 15), die im Java-Plug-In ausgeführt werden, und bei Applikationen mit entsprechenden Sicherheitseinstellungen in der Policy-Datei der verwendeten Java-Laufzeitumgebung. Dem Code muss hierfür das Recht java.io.FilePermission mit der Aktion execute zugewiesen werden.

Der Start eines Prozesses erfolgt mit der Methode exec(). Hiervon gibt es mehrere Varianten, von denen die wichtigsten zwei an dieser Stelle beschrieben werden: Wenn in einer Klasse, die nicht die Rechte dazu besitzt, exec() ausgeführt wird, wird eine SecurityException ausgelöst.

Ist der Aufruf gelungen, liefert exec() als Ergebnis eine Instanz der Klasse Process, welche einen Prozess repräsentiert. Über diese Instanz kann man mit dem neu erzeugten Prozess kommunizieren, d. h. Daten vom Prozess lesen und Daten zum Prozess schreiben.

Dies wird anhand des UNIX-Kommandos »ls« erläutert. Eine Applikation startet das Kommando »ls« und gibt dessen Ergebnis auf der Standardausgabe aus:
  public class ProcessDemo {
  
    public static void main(String args[]) {
      String text ="";    // Lesepuffer
      // Stream zum Einlesen der Prozeßausgabe
      BufferedReader in;
      PrintWriter out = new PrintWriter(System.out);
      try {
        // Prozeß anlegen
        Process p =
              Runtime.getRuntime().exec("/bin/ls -l");
        // Eingabestream holen
        in = new BufferedReader(
           new InputStreamReader(p.getInputStream()));
        // Alle Zeichen aus dem Stream auslesen und
        // auf der Standardausgabe ausgeben
        while ((text = in.readLine()) != null) {
          out.println(text); out.flush();
        }
      }
      catch (IOException e) {
        e.printStackTrace();
      }
    }
  
  }
Zuerst wird mit exec() eine Instanz des Prozesses erzeugt:
  Process p = Runtime.getRuntime().exec("/bin/ls -l");
Der Name des Programms, das gestartet werden soll, muss absolut angegeben werden. Eventuell gesetzte Umgebungsvariablen werden nicht berücksichtigt. Der Pfad ist systemabhängig und muss beim eigenen Test nach Bedarf im Beispiel geändert werden.

Auf einem UNIX-System hat man allerdings die Möglichkeit, folgenden kleinen Trick anzuwenden: Man ruft hierbei das Programm nicht direkt auf, sondern ein Shell-Skript, in dem der Programmaufruf stattfindet. In diesem Fall wird zunächst die Shell gestartet. Innerhalb der Shell werden Umgebungsvariablen initialisiert. Einen anderen Vorteil bietet diese Technik bezüglich Angabe von Platzhaltern. Wenn man bei einem UNIX-Kommando z. B. einen Menge von Dateien mit dem Stern als Platzhalter (*) angibt, so wird der angegebene Parameterstring von der Shell verarbeitet und durch alle Dateinamen, die zutreffen, ersetzt. Bei einem Programmaufruf aus Java ist das also nicht möglich, sofern diese Funktion nicht zusätzlich vom Kommando übernommen wird. Erstellt man allerdings ein Shell-Skript, das vom Java-Programm aufgerufen wird, und ruft innerhalb dieses Skriptes das Programm auf, läuft das Programm wiederum in der Shell, und Wildcards werden wieder ersetzt.

Nach dem Aufruf von exec() ist der Prozess gestartet. Wird das Kommando »ls« von der Konsole ausgeführt, gibt es die Namen der Dateien und Verzeichnisse auf der Standardausgabe aus. Bei Prozessen, die von einem Java-Programm gestartet werden, ist dies nicht der Fall. Die Ausgabe muss also auf eine andere Weise bereitgestellt werden.

Hierzu besitzt die Klasse Process, wie man obigem Listing entnehmen kann, die Methode getInputStream() :
  in = new DataInputStream(p.getInputStream());
Der von getInputStream() gelieferte Stream ist mit dem Ausgabe-Stream des gestarteten Prozesses verbunden. Im obigen Fall wird der InputStream in einen DataInputStream umgewandelt, damit man auf komfortablere Einlesemethoden zugreifen kann.

Aus diesem Stream kann die Ausgabe des Prozesses gelesen werden. Da die Ausgabe im Stream zwischengepuffert wird, ist dies auch der Fall, wenn der Prozess bei Aufruf von getInputStream() schon längst beendet ist.

Danach werden alle Daten, die sich in diesem Stream befinden, eingelesen und anschließend auf der Standardausgabe ausgegeben.

Analog zu getInputStream() besitzt die Klasse Process die Methode getOutputStream(). Diese Methode liefert einen Stream, der mit dem Eingabe-Stream des Prozesses verbunden ist. Indem man in diesen Stream schreibt, kann man an den gestarteten Prozess Daten schicken.

Auf diese Weise ist es natürlich auch möglich, die entsprechenden Systemkommandos zum Löschen von Dateien aufzurufen. Aus diesem Grund ist der Aufruf von exec() den Klassen vorbehalten, die als vertrauenswürdig eingestuft werden.

Eine andere Anwendung für das externe Starten von Prozessen ist z. B. die Integration einer Rechtschreibkorrektur in Java-Anwendungen. ispell ist ein recht verbreitetes Paket zur Durchfürung von Rechtschreibprüfungen in Texten, das auch auf sehr vielen Plattform verfügbar ist. Bei gängigen Linux-Distributionen wird es in der Regel bereits mitgeliefert. Die verfügbaren Java-basierten Rechtschreibkorrekturen sind trotz der ständig verbesserten Geschwindigkeit der Laufzeitumgebungen doch meist in der Anwendung noch etwas träge.


 Inhaltsverzeichnis   Auf Ebene Zurück   Seite Zurück   Seite Vor   Auf Ebene Vor   Eine Ebene höher   Index

Copyright © 2002 dpunkt.Verlag, Heidelberg. Alle Rechte vorbehalten.