Ich denke, die anderen haben gute Arbeit geleistet, um zu erklären, warum cnt> 0 ist, aber es gibt nicht genügend Details darüber, warum cnt = 4 ist und warum cnt in den verschiedenen Einstellungen so stark variiert. Ich werde versuchen, diese Lücke hier zu füllen.
Lassen
- X ist die Gesamtstapelgröße
- M ist der Stapelplatz, der verwendet wird, wenn wir das erste Mal main eingeben
- R ist die Erhöhung des Stapelplatzes bei jedem Eintritt in main
- P ist der zum Ausführen erforderliche Stapelspeicherplatz
System.out.println
Wenn wir zum ersten Mal in die Hauptleitung kommen, bleibt nur noch XM übrig. Jeder rekursive Aufruf beansprucht R mehr Speicher. Für 1 rekursiven Aufruf (1 mehr als das Original) ist die Speichernutzung M + R. Angenommen, StackOverflowError wird nach C erfolgreichen rekursiven Aufrufen ausgelöst, dh M + C * R <= X und M + C * (R +) 1)> X. Zum Zeitpunkt des ersten StackOverflowError ist noch X-M-C * R-Speicher vorhanden.
Um laufen zu können System.out.prinln
, benötigen wir P verbleibenden Speicherplatz auf dem Stapel. Wenn es so kommt, dass X - M - C * R> = P ist, wird 0 gedruckt. Wenn P mehr Speicherplatz benötigt, entfernen wir Frames vom Stapel und gewinnen R-Speicher auf Kosten von cnt ++.
Wenn println
es endlich laufen kann, ist X - M - (C - cnt) * R> = P. Wenn also P für ein bestimmtes System groß ist, ist cnt groß.
Schauen wir uns dies anhand einiger Beispiele an.
Beispiel 1: Angenommen
- X = 100
- M = 1
- R = 2
- P = 1
Dann ist C = Boden ((XM) / R) = 49 und cnt = Decke ((P - (X - M - C * R)) / R) = 0.
Beispiel 2: Angenommen, das
- X = 100
- M = 1
- R = 5
- P = 12
Dann ist C = 19 und cnt = 2.
Beispiel 3: Angenommen, das
- X = 101
- M = 1
- R = 5
- P = 12
Dann ist C = 20 und cnt = 3.
Beispiel 4: Angenommen, das
- X = 101
- M = 2
- R = 5
- P = 12
Dann ist C = 19 und cnt = 2.
Wir sehen also, dass sowohl das System (M, R und P) als auch die Stapelgröße (X) cnt beeinflussen.
Nebenbei bemerkt spielt es keine Rolle, wie viel Platz catch
zum Starten benötigt wird. Solange nicht genügend Platz vorhanden ist catch
, wird cnt nicht erhöht, sodass keine externen Effekte auftreten.
BEARBEITEN
Ich nehme zurück, was ich gesagt habe catch
. Es spielt eine Rolle. Angenommen, zum Starten wird T Speicherplatz benötigt. cnt beginnt zu erhöhen, wenn der verbleibende Raum größer als T ist, und wird ausgeführt, println
wenn der verbleibende Raum größer als T + P ist. Dies fügt den Berechnungen einen zusätzlichen Schritt hinzu und trübt die bereits schlammige Analyse weiter.
BEARBEITEN
Ich fand endlich Zeit, einige Experimente durchzuführen, um meine Theorie zu stützen. Leider scheint die Theorie nicht mit den Experimenten übereinzustimmen. Was tatsächlich passiert, ist ganz anders.
Versuchsaufbau: Ubuntu 12.04 Server mit Standard Java und Standard-JDK. Xss ab 70.000 in Schritten von 1 Byte bis 460.000.
Die Ergebnisse sind verfügbar unter: https://www.google.com/fusiontables/DataSource?docid=1xkJhd4s8biLghe6gZbcfUs3vT5MpS_OnscjWDbM
Ich habe eine andere Version erstellt, in der jeder wiederholte Datenpunkt entfernt wird. Mit anderen Worten, es werden nur Punkte angezeigt, die sich von den vorherigen unterscheiden. Dies erleichtert das Erkennen von Anomalien. https://www.google.com/fusiontables/DataSource?docid=1XG_SRzrrNasepwZoNHqEAKuZlHiAm9vbEdwfsUA