Es gibt einen Fehler in JDK7 bezüglich des CodeCache Speicherbereichs, der uns sehr, sehr hart getroffen hat.
Erklärung
Grundsätzlich Java gestartet und verwendet nur in Time-Kompilierung (JIT) während der Laufzeit nur die benötigten Teile des Bytecode kompiliert. Dadurch kann die JVM bestimmte Codefragmente während der Ausführung de- und rekompilieren. Dies passiert, wenn die JVM feststellt, dass eine Erstkompilierung eines bestimmten Codefragments suboptimal ist. Oracle führte in JDK 7 eine Funktion namens gestaffelte Kompilierung ein, die es der VM ermöglicht, genau das zu tun.
Kompilierter Code in der JVM wird im Speicherbereich CodeCache
gespeichert. Bis JDK6 war der Standard, dass dieser Bereich gefüllt würde und einmal bei 100% würde der JIT aufhören zu kompilieren und ein Fehler würde auf die Konsole gedruckt werden, aber die Anwendung würde genauso laufen wie zuvor: Alles was bereits kompiliert wurde, würde kompiliert bleiben würde alles, was noch nicht kompiliert wurde, im Interpretationsmodus ausgeführt (was ungefähr 100x langsamer ist)
Diese Option heißt CodeCacheFlushing
, sie ist standardmäßig seit JDK7u4 aktiviert. Die Idee ist, dass, sobald CodeCache
voll ist, die am wenigsten verwendeten Teile des kompilierten Codes aus dem Speicher gelöscht werden, um Platz für andere Codefragmente zu schaffen. Das würde das JDK6-Default-Verhalten (um die Kompilierung insgesamt zu stoppen) obsolet machen. Es erlaubt auch einen viel kleineren CodeCache-Bereich (in JDK7 ist CodeCache standardmäßig 48M/96M, wenn gestaffelte Kompilierung aktiviert ist).
Hier kommt der Fehler. Sobald der CodeCache in JDK7 voll wird, wird der JIT gestoppt. Als nächstes kommt das Löschen des CodeCache-Bereichs. Das ist es. JIT sollte wieder aktiviert werden, nachdem die Spülung abgeschlossen wurde, aber das passiert nicht. Außerdem wird keine Warnung auf der Konsole ausgegeben. Schlimmer noch: Vor der Deaktivierung des JIT wird ungefähr die Hälfte des bereits kompilierten Codes verworfen.
Im Gegensatz zu JDK6, wo alles, was schnell war, schnell bleibt und nur neuer Code interpretiert wird, verlieren Sie in JDK7 tatsächlich bereits kompilierten und optimierten Code! Alle Teile Ihrer Anwendung, die sich gut bewährt haben, werden damit aufhören. Es ist dem Zufall überlassen, welche Teile der Anwendung langsamer werden, wodurch das Verfolgen des Buggers durch den Profiler nahezu unmöglich wird: Manchmal verlangsamt sich der Ruhezustandscode für das Spülen, zu anderen Zeiten ist es der Frühjahrs-DI-Code oder Ihr eigener Appcode.
Sind Sie betroffen?
Sie können einen Profiler (JProfiler/YourKit) oder JConsole (JVisualVM wird nicht tun) verwenden, um den Speicherverbrauch des CodeCache-Speicherbereichs zu überwachen. In der Regel wird die CodeCache-Menge committed
sehr nahe an der used
Menge bleiben (sagen wir committed
ist 23mb, verwendet ist 22mb). Während Ihre Anwendung ausgeführt wird, gehen committed
und used
hoch, bis committed
max
erreicht.An diesem Punkt wird used
scharf auf 1/2 - 2/3 von max
fallen. Danach wachsen used
nicht mehr. Da wird der Bug dich treffen. In JConsole, wird es wie folgt aussehen:

Warum ich und nicht alle anderen?
Die Chancen stehen gut, dass Sie JBoss verwenden. Oracle fand schnell heraus, dass es Dinge gibt, die nicht so sein sollten, dass sie standardmäßig tiered compilation
sein sollten - und doch entschied sich Red Hat in seiner unendlichen Weisheit, es wusste es besser und reaktivierte es erneut. Grundsätzlich funktioniert unsere Webapp in Weblogic gut und nur JBoss ist betroffen, denn ohne die gestufte Kompilierung (nicht in Weblogic aktiviert) ist das Wachstum von CodeCache so gering, dass wir selbst nach wochenlangem Betrieb niemals die Schwelle von 48 MB erreichen.
Was kann ich tun?
Zuerst, entscheiden Sie, ob dieser Fehler Sie trifft. Zweitens, erschweren Sie es, dass der Bug Sie schädigt. Wenn Sie CodeCacheFlushing
deaktivieren, wird der Fehler zumindest nicht schlimmer als zuvor. Das Stoppen von tiered compilation
macht es weniger wahrscheinlich, dass der Fehler auf Sie stößt, genauso wie das Erhöhen des verfügbaren CodeCache-Speichers.
Sie können immer versuchen, zu JDK8 zu wechseln, dies scheint nicht betroffen zu sein und Sie könnten auch eine Überwachung in Ihrer Software implementieren, um Sie zu warnen, wenn CodeCache voll ausgeführt wird.
TL; DR
- In JDK 7 nie tiered Kompilierung aktivieren (standardmäßig aktiviert in JBoss deaktiviert)
- in JBoss 7 immer gesetzt
PRESERVE_JAVA_OPTS=true
in standalone.conf
- immer deaktivieren CodeCacheFlushing (
-XX:-UseCodeCacheFlushing
)
- immer packen Sie eine ausreichende Menge an Speicher in CodeCache (
-XX:ReservedCodeCacheSize=xxM
).
Link zum JDK-Problem: http://bugs.java.com/bugdatabase/view_bug.do?bug_id=8012547 –