Die ColdFusion-Anwendung fiel in Spitzenzeiten aus, bei 30 GB freiem RAM: der JVM-Heap, gemessen, behoben und belegt

Eine Adobe-ColdFusion-Anwendung lief bei wenig Verkehr ohne Klagen und lieferte in Spitzenzeiten Fehler und wurde langsam. Wir haben das Problem auf einer echten Datenbank reproduziert, den genauen Punkt gemessen, an dem der JVM-Heap ausgeht, und die Speicherkonfiguration behoben, mit demselben Lasttest vorher und nachher als Beleg.

  • Fehlerschwelle gemessen: 14 gleichzeitige Anfragen
  • Nach der Behebung: 0 Fehler bei 20 gleichzeitigen
  • Der Server hatte 30 GB RAM frei, während die Anwendung ausfiel
Ein großer, fast leerer Rahmen mit der Beschriftung RAM 40 GB; darin ein kleiner Rahmen mit der Beschriftung JVM 1 GB, voll mit schwarzen Blöcken, und vier orange Blöcke, die über seine Oberkante hinauslaufen

Eine Webanwendung auf Adobe ColdFusion mit einer lokalen MariaDB-Datenbank und einem Berichtsexport, der den Speicher belastet

Ausgangslage

Die Anwendung arbeitete bei wenig Verkehr korrekt. In Spitzenzeiten wuchs die Antwortzeit, ein Teil der Anfragen endete mit einem Fehler 500, und in den schlimmsten Fällen brauchte der Server einen Neustart. Die Symptome sahen nach einem überlasteten Anwendungsserver aus, aber die CPU-Auslastung und der freie RAM des Systems erklärten die Ausfälle nicht.

Die Aufgabe: herausfinden, warum die Anwendung unter Last die Stabilität verliert, die Ursache ohne einen unbegründeten Ausbau der Infrastruktur beseitigen und durch Messung belegen, dass die Änderung das Problem löst. Zwei verschiedene Ursachen haben dasselbe Symptom, ein zu kleiner Heap und ein Speicherleck, und sie brauchen verschiedene Lösungen.

Lösung

Wir haben das Problem auf einer echten Datenbank reproduziert: eine MariaDB mit einem Katalog von 40 000 Produkten und ein ColdFusion-Endpunkt, der fünfzehntausend Zeilen liest und daraus einen Bericht baut, den er für die Dauer der Anfrage im Speicher hält. Ein realistisches Export- oder Berichtsmuster, etwa 68 MB Heap je Anfrage.

Eine Rampe von 1 bis 20 gleichzeitigen Anfragen fand den genauen Punkt, an dem der Heap ausgeht, bestätigt durch die ColdFusion-Logs. Eine Prüfung nach der Last schloss ein Leck aus: Der Speicher kehrte auf seinen Ausgangswert zurück, und die lebende Menge nach einer erzwungenen Garbage Collection war winzig. Dann wurde die JVM-Konfiguration an die echte Produktivkonfiguration angepasst, ein größeres Heap-Limit, Limits für Metaspace und Code Cache und ein Heap-Dump bei einem Out-of-Memory-Fehler, und der identische Lasttest wiederholt.

Ergebnisse

Fehlerschwelle vor der Behebung
Erster OutOfMemoryError bei 14 gleichzeitigen Anfragen, mit 1 GB Heap
Vor der Behebung bei 20 gleichzeitigen Anfragen
10 von 20 Anfragen endeten mit einem Fehler 500
Nach der Behebung
1 bis 20 gleichzeitige Anfragen: jede Antwort 200, null Fehler, mit 20 GB Heap
Spitzenauslastung des Heaps nach der Behebung
Etwa 4,8 GB von 18,2 GB verfügbar
Lebende Menge nach einer erzwungenen Garbage Collection
Etwa 140 MB, also kein Leck
Freier RAM des Servers während der Ausfälle
Etwa 30 GB von 40 GB, also war der Heap das Problem, nicht der RAM

So sind wir vorgegangen

  1. Auf einer echten Datenbank reproduzieren

    Eine MariaDB mit 40 000 Produkten und ein Berichtsendpunkt, der 15 000 Zeilen liest und das Ergebnis für die Dauer der Anfrage im Speicher hält, etwa 68 MB Heap je Anfrage.

  2. Die Schwelle messen

    Eine Rampe von 1 bis 20 gleichzeitigen Anfragen, mit HTTP-Codes, Heap-Auslastung und jedem OutOfMemoryError in den ColdFusion-Logs.

  3. Ein Leck ausschließen

    Nach der Last kehrte der Heap auf seinen Ausgangswert zurück, und die lebende Menge nach einer erzwungenen Garbage Collection lag bei etwa 140 MB. Ein größerer Heap behebt also die Ursache, statt sie zu verschieben.

  4. Die JVM-Konfiguration korrigieren

    Die Werte der echten Produktivkonfiguration: ein größeres Heap-Limit, Limits für Metaspace und Code Cache und ein Heap-Dump bei einem Out-of-Memory-Fehler auf einem Volume mit Platz dafür.

  5. Den identischen Test wiederholen

    Dieselbe Rampe nach dem Neustart: Jede Anfrage von 1 bis 20 gleichzeitigen antwortete 200, das Dump-Verzeichnis blieb leer.

  6. Die Diagnostik eingeschaltet lassen

    Der nächste Out-of-Memory-Fehler, falls er je kommt, hinterlässt einen Dump zur Analyse. Das Verzeichnis ist geschützt, weil ein Dump Anwendungsdaten enthalten kann.

Herausforderungen, die wir gelöst haben

Mit der Standardkonfiguration, einem Heap-Limit von 1 GB, war die Rampe eindeutig: Bis etwa 12 gleichzeitige Berichte arbeitete die Anwendung fehlerfrei, bei 14 ging der Heap aus, und bei 20 scheiterte die Hälfte der Anfragen. Die Logeinträge unten sind so zitiert, wie die Logs sie geschrieben haben; verallgemeinert sind nur der Dateipfad und die Adresse im Threadnamen.

Die genauen Logeinträge

Das Anwendungslog, application.log, nennt die Stelle im CFML-Code: Zeile 24 ist die Abfrage, die aus der Datenbank liest, die Zeilen 33 und 36 bauen die Berichtsstruktur im Speicher. Mehrere Arbeitsthreads kippen in derselben Sekunde, was zeigt, dass die Gleichzeitigkeit die Ursache ist:

"Error","ajp-nio-...-exec-1","10/17/26","14:30:21","","Java heap space The specific sequence of files included or processed is: /app/wwwroot/report.cfm, line: 24"
"Error","ajp-nio-...-exec-13","10/17/26","14:30:21","","GC overhead limit exceeded The specific sequence of files included or processed is: /app/wwwroot/report.cfm, line: 36"
"Error","ajp-nio-...-exec-2","10/17/26","14:30:21","","GC overhead limit exceeded The specific sequence of files included or processed is: /app/wwwroot/report.cfm, line: 33"

Das Ausnahmelog, exception.log, bestätigt den Fehlertyp in beiden Varianten:

java.lang.OutOfMemoryError: Java heap space
java.lang.OutOfMemoryError: GC overhead limit exceeded

Der vollständige Stacktrace in coldfusion-error.log zeigt das Detail, auf das es ankommt. Die obersten Rahmen liegen im MariaDB-Treiber beim Lesen des Ergebnispakets; darunter die Abfrageschicht von ColdFusion:

SEVERE: Servlet.service() for servlet [CfmServlet] in context with path [] threw exception [ROOT CAUSE:
java.lang.OutOfMemoryError: Java heap space
    at org.mariadb.jdbc.client.socket.Reader.readPacket(Reader.java:119)
    at org.mariadb.jdbc.client.result.CompleteResult.<init>(CompleteResult.java:100)
    at org.mariadb.jdbc.message.ClientMessage.readPacket(ClientMessage.java:373)
    at org.mariadb.jdbc.client.impl.StandardClient.readResults(StandardClient.java:1463)
    at org.mariadb.jdbc.client.impl.StandardClient.readResponse(StandardClient.java:1382)
    at org.mariadb.jdbc.client.impl.StandardClient.execute(StandardClient.java:1306)
    at org.mariadb.jdbc.ClientPreparedStatement.execute(ClientPreparedStatement.java:270)
    at coldfusion.server.j2ee.sql.JRunPreparedStatement.execute(JRunPreparedStatement.java:98)
    at coldfusion.sql.Executive.executeQuery(Executive.java:1461)
    at coldfusion.sql.SqlImpl.execute(SqlImpl.java:368)
    at coldfusion.tagext.sql.QueryTag.executeQuery(QueryTag.java:1175)

Im Moment des Fehlers holte die Anfrage das Abfrageergebnis und materialisierte es im Speicher. Eine offene Abfrage hält die Anfrage aktiv, und ein großes Ergebnis, in den Heap gezogen, kippt ihn, wenn viele solche Anfragen zugleich laufen. Dasselbe Muster zeigt sich in den Rahmen, die die Berichtsstruktur bauen, Zeilen 33 und 36: Der Heap geht dort aus, wo gerade Speicher angefordert wird.

„GC overhead limit exceeded“ kommt vor „Java heap space“

Die Logs begannen nicht mit der Meldung, nach der alle suchen. Zuerst, viele Male, kam die andere Variante desselben Fehlers, GC overhead limit exceeded. Sie bedeutet, dass der Garbage Collector mehr als 98 Prozent seiner Zeit damit verbrachte, weniger als 2 Prozent des Heaps freizugeben: Die Anwendung lebte formal und stand praktisch. Erst später kam das harte Java heap space. Wer die Logs nur nach dieser Phrase durchsucht, übersieht die frühere und weit häufigere Warnung.

Der Fehler trifft unterhalb des ColdFusion-Limits für gleichzeitige Anfragen

Der Fehler trat bei 14 gleichzeitigen Anfragen auf, unterhalb des Standardlimits von 25 gleichzeitigen Template-Anfragen. Die Folgerung ist nicht intuitiv: Dieses Limit anzuheben hilft nicht, es lässt mehr schwere Anfragen zugleich den Heap nutzen und bringt den Ausfall früher. Anfragen zu drosseln ersetzt keinen richtig bemessenen Heap.

Steigende Speichernutzung ist noch kein Leck

Nach der Last kehrte der Heap auf etwa 420 MB zurück. Nach der Behebung, mit dem großen Heap, konnte die Nutzung in Ruhe 4,5 GB zeigen, was auf den ersten Blick nach einem Leck aussieht. Nach einer erzwungenen Garbage Collection fiel die lebende Menge auf etwa 140 MB; der Rest war nicht eingesammelter Müll, weil der Collector bei einem großen Heap keine Eile hat. Die Unterscheidung zählt: Wäre die Ursache ein Leck gewesen, hätte ein größerer Heap den Ausfall nur verschoben.

Ein Heap-Dump auf einer vollen Partition ist ein Dump, der nie ankommt

Wir haben den Heap-Dump bei einem Out-of-Memory-Fehler eingeschaltet, damit jeder künftige Ausfall eine Datei zur Analyse hinterlässt. Die Falle: Bei einem Heap von 20 GB kann der Dump mehr als zehn Gigabyte groß werden, und die Systempartition hat selten so viel frei. Der Dump, der nach dem Ausfall am meisten gebraucht wird, würde still nicht geschrieben. Der Dump-Pfad zeigt deshalb auf ein Volume mit echter Reserve.

Ein Limit von 20 GB, und die JVM zeigt 18,2 GB

Nachdem das Limit auf 20 GB gesetzt war, meldete ColdFusion einen maximalen Heap von etwa 18 205 MB. Das ist kein Fehler und kein verlorener Speicher: Beim parallelen Garbage Collector lässt die Zahl, die die Anwendung sieht, einen der Survivor-Bereiche aus. Die fehlenden zwei Gigabyte sind da, sie zählen nur nicht zum sichtbaren Limit. Derselbe Effekt zeigt sich beim Standard-Heap, der etwa 911 MB von 1024 meldet, wie im Installationsprojekt beschrieben.

Ein Balkendiagramm gleichzeitiger Anfragen von 2 bis 20: volle schwarze Balken bis 12, ab 14 orange umrandete Balken mit je einem orangen Kreuz darüber, und eine gestrichelte Schwellenlinie zwischen 12 und 14

Derselbe Lasttest vorher und nachher

Gleichzeitige Anfragen Vor der Behebung, 1 GB Heap Nach der Behebung, 20 GB Heap
1 bis 12 Alle Antworten 200 Alle Antworten 200
14 Erster OutOfMemoryError, 4 Fehler Alle Antworten 200
16 6 Fehler Alle Antworten 200
18 8 Fehler Alle Antworten 200
20 10 Fehler Alle Antworten 200, Spitze des Heaps etwa 4,8 GB

Was wir daraus mitnehmen

  • Den JVM-Heap überwachen, nicht nur den RAM des Servers. Freier Systemspeicher und freier Heap sind verschiedene Größen; eine Anwendung kann bei 30 GB freiem RAM ausfallen.
  • Auf beide Varianten des Fehlers alarmieren. GC overhead limit exceeded ist das frühere Signal, Java heap space das spätere.
  • Die Drosselung von Anfragen nicht mit einer Lösung verwechseln. Tritt der Fehler unterhalb des Limits gleichzeitiger Anfragen auf, verschlimmert ein höheres Limit die Sache.
  • Einen kleinen Heap von einem Leck unterscheiden. Prüfen, ob der Speicher nach der Last zurückkehrt und wie groß die lebende Menge nach einer erzwungenen Garbage Collection ist. Ein größerer Heap heilt einen kleinen Heap, kein Leck.
  • Den Heap nach Messungen und nach dem RAM bemessen. Weder zu klein, das heißt Fehler, noch zu groß, das hungert das System aus und verlängert die Pausen einer vollen Collection. 20 GB waren auf einem Server mit 40 GB sicher; auf einem kleineren wären sie ein Fehler.
  • Die Diagnostik vorbereiten, bevor sie gebraucht wird. Den Heap-Dump einschalten und auf ein Verzeichnis mit echter Reserve zeigen lassen.
  • Durch Messung belegen. Derselbe Lasttest vorher und nachher macht aus „scheint zu funktionieren“ eine Zahl.

Quellen

Zwei Heaps nebeneinander: ein schmaler mit der Beschriftung 1 GB, bis oben voll, mit orangen Blöcken über seiner Kante, und ein breiter mit der Beschriftung 20 GB, der zu einem Viertel gefüllt ist, mit einer gestrichelten Linie am Füllstand

JVM-Einstellungen vorher und nachher (Stand 2026-10)

Einstellung Vorher Nachher
Minimale Heap-Größe, -Xms 256 MB 256 MB
Maximale Heap-Größe, -Xmx 1 024 MB 20 480 MB
-XX:MaxMetaspaceSize Nicht gesetzt 2 560 MB
-XX:ReservedCodeCacheSize Nicht gesetzt 256 MB
Diagnostik Keine -XX:+HeapDumpOnOutOfMemoryError mit einem Dump-Pfad auf einem Volume mit Reserve
Umgebung Adobe ColdFusion 2025, Java 21 mit dem parallelen Garbage Collector, MariaDB 10.11

Verwandte Ausgangslagen

  • Ihr Entwickler hat gekündigt oder ist bereits ausgeschieden, und das System muss weiterlaufen. Wir prüfen das System und legen Ihnen 48 Stunden nach Zugang einen schriftlichen Befund vor.

  • Für ColdFusion 2021 gibt es laut Adobe seit dem 10.11.2025 keine Sicherheits-Patches mehr. Wir vergleichen vier Wege: bleiben und absichern, aktualisieren, zu Lucee wechseln oder schrittweise ablösen, mit Terminen und Risiken (Stand: 2026-10).

Weitere Projekte

  • Eine Punkteskala mit gestrichelter Schwelle: Der Phishing-Umschlag wandert in einem orangen Bogen über die Schwelle

    Ein Spamfilter, der lief, aber nicht schützte

    Dienste liefen, die Konfiguration war gültig, im Systemstatus stand kein Fehler. Trotzdem landete Phishing im Posteingang, während Newsletter von Partnern im Spam lagen. Unsere Ingenieure haben gemessen, welche Erkennungsmechanismen tatsächlich ansprangen, die Ursachen behoben und drei Monate später eine Regression gefunden und behoben, die sich nur stundenweise zeigte.

  • Vier Briefumschläge passieren die Tore SPF, DKIM und DMARC, jeweils orange abgehakt, auf dem Weg in den Posteingang

    E-Mail-Zustellbarkeit für vier Firmendomains

    Ein Mailserver für vier Firmendomains signierte nichts, veröffentlichte keine DMARC-Richtlinie und wurde von Microsoft abgewiesen. Unsere Ingenieure haben ihn geprüft, die Änderung an einer Domain erprobt und in Gruppen ausgerollt. Das Ergebnis: Alle drei Prüfungen bestehen bei Gmail, die Microsoft-Sperre ist aufgehoben, und die Nutzer haben nichts bemerkt.

In 20 Minuten klären wir, welcher erste Schritt zu Ihrem System passt

Sie schildern Ziel und Stand, wir sagen offen, ob und wie wir helfen können. Antwort innerhalb eines Werktags, unverbindlich.

Telefon (Deutsch, Polnisch, Englisch) +48 501 250 220
E-Mail E-Mail

Erreichbar: Montag bis Freitag, 9:00 bis 17:00 Uhr (MEZ); Support und Notfälle rund um die Uhr (24/7)