Von dem, was Linux-Kernel / libc Version ist Java Runtime.exec () sicher in Bezug auf Speicher?

StackOverflow https://stackoverflow.com/questions/209875

Frage

Bei der Arbeit einer unserer Zielplattformen ist eine ressourcenbeschränkte Mini-Server unter Linux (Kernel 2.6.13, kundenspezifische Verteilung basiert auf einer alten Fedora Core). Die Anwendung ist in Java (Sun JDK 1.6_04) geschrieben. Der Linux-OOM Killer ist so konfiguriert, Prozesse zu töten, wenn die Speichernutzung 160MB überschreitet. Selbst bei hohen Belastung unserer Anwendung nie über 120MB gehen und zusammen mit einigen anderen nativen Prozessen, die aktiv sind, bleiben wir auch innerhalb der OOM Grenze.

Es stellt sich jedoch heraus, dass die Java Runtime.getRuntime (). Exec () -Methode, die kanonische Weise externe Prozesse von Java auszuführen, eine besonders bedauerlich Implementierung auf Linux , die gelaicht Kind Prozesse (vorübergehend) verursacht, um die gleiche Menge an Speicher als Prozess Eltern da der Adressraum kopiert wird. Das Nettoergebnis ist, dass unser Antrag des OOM Killer getötet wird, sobald wir Runtime.getRuntime tun (). Exec ().

Wir arbeiten zur Zeit um diesen durch ein separates nativen Programm mit tut alle externe Befehlsausführung und wir kommunizieren mit diesem Programm über eine Steckdose. Dies ist weniger als optimal.

Nach dem Posting zu diesem Problem Online ich einige bekam vermutlich anzeigt Feedback, dass dies nicht auf „neueren“ Linux-Versionen auftreten sollten, da sie die Posix-fork () Methode unter Verwendung von copy-on-write implementieren, dh es werden nur Seiten kopiert, die es braucht zu ändern, wenn es anstelle des gesamten erforderlich ist Adressraum sofort.

Meine Fragen sind:

  • Ist das wahr?
  • Ist das etwas im Kernel, die libc Implementierung oder ganz woanders?
  • Von welcher Version des Kernels / libc / was auch immer ist copy-on-write für fork () verfügbar?
War es hilfreich?

Lösung

Das ist so ziemlich die Art und Weise * nichts (und Linux) sind seit Anbeginn der Zeit (oder atleat Anbeginn der MMU) gearbeitet.

ein neues Verfahren zu schaffen, auf * nixes Sie fork () aufrufen. fork () erstellt eine Kopie des aufrufenden Prozesses mit all seinen Speicherzuordnungen, Dateibeschreibungen usw. Die Speicherzuordnungen copy-on-write so getan werden (in optimalen Fällen) kein Speicher tatsächlich kopiert wird, nur die Zuordnungen. A folgend exec () -Aufruf ersetzt die aktuelle Speicherzuordnung mit den der neuen ausführbaren Datei. Also, fork () / exec () ist so, wie Sie einen neuen Prozess erstellen und das ist, was die JVM verwendet.

Der Nachteil mit großen Prozesse auf einem ausgelasteten System ist, kann der Elternteil weiterhin für eine kleine Weile, bevor das Kind exec () 's verursacht eine riesige Menge an Speicher kopiert werden Ursache des copy-on-write laufen. In VMs kann den Speicher um eine Menge bewegt werden, um einen Garbage Collector zu erleichtern, die noch mehr Kopieren erzeugt.

Die „Abhilfe“ zu tun, was Sie bereits getan haben, einen externen leichten Prozess erstellen, neue Prozesse Laichen kümmert - oder einen leichten Ansatz als fork / exec verwendeten Prozesse, um zu laichen (Welche Linux nicht - und wäre ohnehin eine Änderung der Jvm erfordert selbst). Posix gibt die posix_spawn () Funktion, die in der Theorie kann, ohne das Kopieren der Speicherzuordnung des anrufenden Prozess implementiert werden. - aber auf Linux ist es nicht

Andere Tipps

Nun, bezweifle ich persönlich, dass dies wahr ist, da Linux fork () per Copy-on-Write getan wird, da Gott weiß, wann (zumindest die Kernel 2.2.x es hatte, und es war irgendwo in der 199x).

Da OOM Killer geglaubt wird, ein eher grobes Instrument zu sein, das bekannt ist, dass Fehlzündungen (fe, ist es nicht notwendig, den Prozess tötet, die tatsächlich die meisten der Speicher zugewiesen) und die nur als letztes Mittel resport verwendet werden soll, ist es mir nicht klar, warum haben Sie es konfiguriert auf 160M abzufeuern.

Wenn Sie eine Grenze für Speicherzuweisung verhängen wollen, dann ulimit ist dein Freund, nicht OOM.

Mein Rat ist OOM allein zu lassen (oder ganz deaktivieren), konfiguriert ulimits, und vergessen Sie dieses Problem.

Ja, das ist absolut der Fall auch neue Versionen von Linux (wir sind auf 64-Bit Red Hat 5.2). Ich habe seit rund 18 Monaten ein Problem mit langsam laufenden Teilprozessen mit und konnte nie das Problem, herauszufinden, bis ich Ihre Frage gelesen und lief einen Test um es zu überprüfen.

Wir haben eine 32 GB-Box mit 16 Kernen, und wenn wir die JVM mit Einstellungen wie -Xms4g und -Xmx8g und lief Subprozesse mit Runtime.exec () mit 16 Fäden laufen, sind wir nicht in der Lage sein, unseren Prozess schneller laufen als etwa 20 Prozessaufrufe pro Sekunde.

Versuchen Sie dies mit dem einfachen „date“ Befehl in Linux über 10.000 Mal. Wenn Sie in Profilierungs Code hinzufügen zu beobachten, was geschieht, beginnt es schnell ab, aber verlangsamt sich im Laufe der Zeit.

Nach der Frage zu lesen, habe ich beschlossen, mein Gedächtnis senken Einstellungen, um zu versuchen zu -Xms128m und -Xmx128M. Jetzt ist unser Prozess läuft bei etwa 80 Verfahren pro Sekunde nennt. Die JVM-Speicher-Einstellungen war alles, was ich geändert.

Es scheint nicht so, Speicher Saugen bis zu sein, die ich je aus der Erinnerung lief, auch wenn ich versuchte es mit 32 Threads. Es ist nur der zusätzliche Speicher in irgendeiner Weise zugeordnet werden, was einen schweren Anlauf verursacht (und vielleicht Shutdown) Kosten.

Wie auch immer, scheint es, wie es eine Einstellung sein soll dieses Verhalten Linux oder vielleicht sogar in der JVM zu deaktivieren.

1: Ja. 2: Dieses gliedert sich in zwei Schritte von: Jede Systemaufruf wie fork () durch die glibc zum Kernel gewickelt ist. Der Kernel-Teil des Systems-Anruf ist in kernel / fork.c 3: Ich weiß es nicht. Aber ich würde wetten, dass Ihr Kernel hat.

Die OOM Killer Tritte in, wenn Low-Speicher auf 32-Bit-Boxen bedroht ist. Ich habe noch nie ein Problem mit diesem gehabt, aber es gibt Möglichkeiten, OOM in Schach zu halten. Dieses Problem könnte einig OOM Konfigurationsproblem sein.

Da Sie eine Java-Anwendung verwenden, sollten Sie in Erwägung ziehen Linux 64bit. Das sollte auf jeden Fall beheben. Die meisten 32-Bit-Anwendungen können ohne Probleme auf einem 64-Bit-Kernel laufen, solange relevant Bibliotheken installiert sind.

Sie können auch den PAE-Kernel versuchen, für 32-Bit-Filzhut.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top