Warum ist die Heap-Größe für JVMs festgelegt?

20

Kann mir jemand erklären, warum JVMs (ich habe nicht zu viele geprüft, aber ich habe noch nie eine gesehen, die das nicht so gemacht hat) auf einer festen Heap-Größe ausgeführt werden müssen? Ich weiß, dass die Implementierung auf einem einfachen zusammenhängenden Heap einfacher ist, aber die Sun-JVM ist inzwischen über ein Jahrzehnt alt, und ich würde davon ausgehen, dass sie Zeit hatten, dies zu verbessern.

Die Notwendigkeit, die maximale Speichergröße Ihres Programms zum Startzeitpunkt zu definieren, scheint in den 1960er Jahren ein Problem zu sein, und dann gibt es die schlechten Wechselwirkungen mit der virtuellen Speicherverwaltung des Betriebssystems (GC-Abruf von ausgelagerten Daten, Unfähigkeit zu bestimmen, wie viel Speicher der Java-Prozess ist) wirklich mit von der OS - Seite, verschwendet große Mengen an VM Raum (ich weiß, kümmert es dich nicht auf deine Phantasie 48bit Maschinen ...)). Ich vermute auch, dass die verschiedenen traurigen Versuche, kleine Betriebssysteme innerhalb der JVM (EE Application Server, OSGi) zu erstellen, zumindest teilweise auf diesen Umstand zurückzuführen sind, da das Ausführen mehrerer Java-Prozesse auf einem System ausnahmslos zu Ressourcenverschwendung führt, da dies erforderlich ist Geben Sie jedem Speicher den Speicher an, den er zu Spitzenzeiten verwenden muss.

Überraschenderweise hat Google darüber nicht die erwarteten Empörungsstürme ausgelöst, aber sie wurden möglicherweise unter den Millionen von Menschen begraben, die sich über eine feste Haufengröße informierten und diese Tatsache einfach akzeptierten.

themel
quelle
Das Design von EE-Anwendungsservern würde auch ohne diesen "Umstand" durchaus Sinn machen, da die JVM selbst etwas Speicherplatz benötigt und das Wechseln zwischen Threads billiger ist als das Wechseln zwischen Prozessen.
Michael Borgwardt
Das ist eine Art Scherz, und ich frage mich, wie viel ich darüber nachgedacht habe. Wie würde sich beispielsweise die GC / Swap-Interaktion ändern, wenn Sie kein Heap-Limit hätten? Wie wird VM-Speicherplatz verschwendet? Sie erhalten Ihren 2 / 3Gb-Speicherplatz, ob Sie ihn verwenden oder nicht, und wenn Sie die Grenzen dieses Speicherplatzes überschreiten, spielt es keine Rolle, ob Sie einen festen oder einen schwebenden Heap haben. Wie verschwenden mehrere JVMs etwas anderes als Swap (das für den Zweck der Maschine entsprechend konfiguriert werden sollte)?
kdgregory
2
Es ist eine Art Rant, aber es wird durch jahrelange tägliche Erfahrung mit dem Schreiben und Betreiben einer Java-basierten Plattform bestimmt. Wenn Sie nicht tauschen (weil Ihr System 20 Minuten lang nicht mehr reagiert, bis nicht mehr genügend Speicherplatz für die Zuordnung zur Verfügung steht) und die Speicherüberlastung aus Stabilitätsgründen deaktiviert ist (der OOM-Killer ist nicht sehr gut darin, Opfer auszuwählen) Wenn Sie eine Java-VM mit -Xmx2048m starten, werden sofort 2 GB virtueller Speicher (zumindest in meiner Sun JVM unter Linux) für ein Programm mit einer Variablen zugewiesen.
Thema
Hervorragende Frage. Ich habe mich das selbe gefragt. Aber welche der hier in q dargestellten "Fakten" und die Antworten sind richtig?
Martin Ba
Für die Reaktionen, nach denen Sie gesucht haben, gehen Sie einfach zum Sun Bug Tracker ... zB hier , hier und hier . Lesen Sie diese und fühlen Sie die empörte Wut :)
Basic

Antworten:

23

Sie liegen falsch. Die Größe des Heapspeichers von JVMs ist nicht festgelegt, sondern nur begrenzt:

  • -Xmx legt die maximale Größe des Heapspeichers fest
  • -Xms legt die minimale Größe des Heapspeichers fest

Das Festlegen einer Obergrenze ist aus mehreren Gründen erforderlich. Erstens teilt es dem Müllmann mit, wann er in Aktion treten soll. Zweitens wird verhindert, dass die JVM die gesamte Maschine verstopft, indem zu viel Speicher verbraucht wird. Die minimale Größe des Heapspeichers ist wahrscheinlich nützlich, um die Menge an Speicher zu reservieren, die das Programm mindestens benötigt, um zu verhindern, dass ihm der Speicher ausgeht (weil andere Prozesse zu viel Speicher verbrauchen).

user281377
quelle
9
nicht die min ist der Standard eine langsame Start zu vermeiden , wenn eine Menge , was zu wiederholten Haufen erhöht zuzuteilen muss, Paging wird mit dem physischen RAM umgehen läuft aus
Ratsche Freak
@ Ratchetfreak, das war meine zweite Vermutung ;-)
User281377
@ user281377 Wenn dies der Fall ist, wie kann C # ohne maximale Größe des Heapspeichers problemlos ausgeführt werden?
cmorse
cmorse: Ich kann nur raten. Vielleicht zielt Java auf große Server ab, auf denen viele Anwendungen Ressourcen gemeinsam nutzen und streng erzwungene Beschränkungen wünschenswert sind, während .net eher für PCs und kleinere, dediziertere Server gedacht ist.
user281377
@ user281377: Die von mir verwendeten Java-Anwendungen, denen der Heap-Speicherplatz ausgeht, handhaben dies im Allgemeinen sehr schlecht, stürzen im Allgemeinen nur ab oder sind danach sehr unauffällig. Und ASP.net läuft sowohl auf großen als auch auf kleinen Servern einwandfrei. Was ich wirklich nicht verstehe, ist, warum Java dieses Limit standardmäßig erzwingt. Ich würde gerne von den Gründen für ihre Entscheidung hören ... ich bin mir sicher, dass sie einen guten Grund hatten.
cmorse
6

Ich stelle mir vor, die Antwort hat etwas mit Javas Erbe zu tun. Es wurde ursprünglich als Sprache für eingebettete Systeme entwickelt, bei denen die Ressourcen offensichtlich begrenzt sind und Prozesse nicht einfach das verschlingen sollen, was verfügbar ist. Dies hilft auch bei der Systemadministration, da es einfacher ist, Ressourcen auf einem Server bereitzustellen, wenn Sie Ressourcenlimits festlegen können. Ich sehe, dass die neuesten JVMs anscheinend mehrere nicht fortlaufende Heaps verwenden, obwohl natürlich alles als ein einziger Heap für Ihren Code angezeigt wird.

(FWIW, Sie mussten den Speicherbedarf eines Programms unter den MacOS-Versionen vor Darwin von Apple angeben [bis auf System 7, das das letzte war, das ich verwendet habe], das bis weit in die 80er Jahre reicht.)

TMN
quelle
1
+1 - weil es (1) die einzige Antwort ist, die die Frage tatsächlich beantwortet hat, und (2) plausibel ist.
kdgregory
2

Sie müssen dem GC einen Mechanismus geben, der angibt , wann er ausgeführt werden soll. Andernfalls füllt Ihr Programm den gesamten virtuellen Speicherplatz aus. Es gibt viele alternative Möglichkeiten, um GC auszulösen: verstrichene Zeit, Anzahl der Zuweisungen, Anzahl der Zuweisungen, wahrscheinlich andere, die mir derzeit nicht einfallen. IMO ist keines davon so gut wie das Festlegen einer Speichergrenze und das Ausführen von GC, wenn der zugewiesene Speicherplatz diese Grenze erreicht.

Der Schlüssel ist, die richtigen Grenzen zu setzen. Ich betrachte -msals "das ist, wie viel Speicher meine App benötigt", und -mxals "es sollte nie diese Menge überschreiten." In einer Produktionsbereitstellung sollten beide nahe beieinander liegen, wenn sie nicht gleich sind, und sie sollten auf tatsächlichen, gemessenen Anforderungen basieren.

Ihre Bedenken hinsichtlich "verschwendeten" virtuellen Speichers sind falsch platziert: Es ist virtuell, es ist (fast) kostenlos. Ja, wenn der Heap zu groß ist, können Sie nicht so viele Threads starten oder so viele Dateien mit Speicherzuordnung laden. Dies ist jedoch Teil des Anwendungsdesigns: Sie haben knappe Ressourcen und müssen diese so partitionieren, dass Ihre Anwendung ausgeführt werden kann. In einem Heap im C-Stil, der sich ausdehnt, bis Sie die Speicherkapazität erreicht haben, ist das Grundproblem dasselbe. Sie müssen nur nicht darüber nachdenken, bis Sie in Schwierigkeiten sind.

Das einzige, was ein großer Heap "verschwenden" könnte, ist der Swap-Speicher, da für alle beschreibbaren Segmente ein Commit von Swap erforderlich ist. Dies ist jedoch Teil des Systemdesigns: Wenn Sie möchten, dass viele JVMs auf derselben Box ausgeführt werden, erhöhen Sie entweder Ihren Swap, oder verringern Sie die Heap-Zuweisung. Wenn sie zu krachen beginnen, versuchen Sie, zu viel mit dem System zu tun. Kaufen Sie mehr Speicher (und wenn Sie noch einen 32-Bit-Prozessor verwenden, kaufen Sie eine neue Box).

kdgregory
quelle
1
Der Schwellenwert für die Ausführung von GC muss jedoch nichts mit einem festen Schwellenwert zu tun haben, den der gesamte vom Programm verwendete Speicher nicht überschreiten kann. (In der Tat sollte dies wahrscheinlich nicht der Fall sein. Wenn Sie einen großen Haufen haben und nur GC können, wenn dieser voll ist, müssen Sie seltene, aber lange GC-Perioden haben.) Siehe Speicherbereinigung für Generationen.
Ben
@Ben - ja, du hast recht. Und mein zweiter Satz weist darauf hin, dass es Alternativen gibt. Ich stimme jedoch nicht zu, dass ein Heap mit fester Größe im Allgemeinen der falsche Weg ist, um GC zu verwalten. Eine richtig optimierte JVM verwendet die "richtige" Heap-Größe. Nach meiner Erfahrung passieren lange GC-Pausen, wenn die JVM nicht richtig eingestellt wurde. Und oft ist die "richtige" Heap-Größe viel kleiner als Sie vielleicht denken.
kdgregory
-2

Wie sagt user281377 geben Sie nur eine Obergrenze, wie viel Speicher Ihr Prozess kann verbrauchen. Natürlich greift die Anwendung selbst nur auf den benötigten Speicherplatz zu.

Ob es eine Standardobergrenze geben sollte oder nicht, ist eine andere Frage, sowohl für als auch gegen.

dagnelies
quelle