Memory Guard Application
Zielstellung
In speziellen seltenen Fällen kann es erforderlich werden, dass der Arbeitsspeicher und seine Auslastung automatisch überwacht wird, um Performance-Probleme des Lobster_data zu verhindern, die nach längerer ununterbrochener Laufzeit auftreten.
Speziell sei hier die Situation genannt, dass Treibersoftware fremder Hersteller vom Lobster_data / Integration Server angesprochen wird, die eine native Library (unter Windows: DLL) verwenden, z. B. der SAP JCo oder auch Datenbank-Treiber.
Vereinzelt gibt es hier den Fehler, dass fortwährend kleine Teile des Arbeitsspeichers, die von dieser Library verwendet werden, nach Verwendung nicht mehr frei gegeben werden. Das führt dazu, dass der freie RAM ständig abnimmt.
Dieses Problem kann prinzipiell behoben werden, indem man den Lobster IS/Lobster_data in regelmäßigen Abständen neu startet, die so gewählt werden sollten, dass es garantiert nicht zum Speicherengpass kommt.
In vielen Fällen ist aber ein langfristig ununterbrochener Betrieb gewünscht oder erforderlich oder der regelmäßige Neustart wird einfach vergessen und dadurch entsteht eventuell sogar die Gefahr eines Systemabsturzes, wenn der freie Arbeitsspeicher nicht ständig überwacht wird.
Eine „manuelle“ Überwachung des freien Speichers wäre immer nur ein Augenblicks-Bild und natürlich kann bei einem stark beschäftigten System der Speicherbedarf bis zu 80% oder 90% ansteigen. Es ist auch möglich, dass diese hohe Auslastung über einige Zeit andauert. Deshalb ist eine Entscheidung, wann ein Problem besteht, nicht durch ein Augenblicks-Bild oder nach "Bauchgefühl" richtig, sondern nur durch einen systematisch arbeitenden Algorithmus, der den Speicher ununterbrochen und in dichter Folge prüft und dann definierte mathematische Regeln anwendet.
Diese Erkenntnis führte zur Entwicklung der Memory Guard Application. Diese Anwendung ist ein Zusatz-Tool zum Lobster IS/Lobster_data, die nur gebraucht wird, wenn es nach längerer Laufzeit des Lobster_data zu Memory Leaks kommt.
Abgrenzung
Die Memory Guard Application kann weder Memory Leaks verhindern, noch selbständig beheben. Sie liefert nach richtiger Konfiguration nur ein Kriterium für die Entscheidung, ob und wann der Neustart durch einen Administrator ausgeführt werden sollte. Bei Überschreiten der konfigurierbaren "roten Linie" wird ein E-Mail versendet. Der E-Mail-Versand wird in regelmäßigen Abständen wiederholt, wenn die Fehlersituation weiter besteht.
Zusätzlich wird ein E-Mail bei jedem Neustart der Memory Guard Application an die gleiche E-Mail-Adresse aber mit anderem Betreff und Text versendet,
um die System-Verantwortlichen über einen erfolgten Neustart zu unterrichten und
um dem Mail-Kanal zu prüfen.
Bei automatischer Auswertung der Emails kann man am Betreff unterscheiden, ob es ein Neustart oder eine Alarmsituation ist und von welchem System die Meldung erfolgt.
Funktionsprinzip
Speicherverwaltung der Java Virtual Machine
Innerhalb der Java Virtual Machine (JVM), die das Programm des Lobster IS/Lobster_data ausführt, wird die Speicherverwaltung von einem Subsystem der JVM übernommen. Dieses Subsystem ist auch dafür verantwortlich, den Speicher wieder frei zu geben, der von Java-Objekten verwendet wurde, nachdem diese Objekte nicht mehr gebraucht werden. Diese Freigabe des Speichers heißt Garbage Collection und das Subsystem dazu heißt Garbage Collector. Dadurch muss sich der Programmierer eines Java-Programmes nicht mehr um die Freigabe des Speichers kümmern und weil nun keine Fehler mehr gemacht werden können, die zu "totem Speicher" führen, funktioniert das Konzept der Garbage Collection sehr zuverlässig. Ein langsames Anwachsen des verwendeten Speichers durch vergessene Speicher-Freigaben tritt praktisch auf.
Da der Garbage Collector selbst auch mit dem Anwendungsprogramm um die CPU konkurriert, wäre zu häufiger Lauf des Garbage Collectors ein Performance-Problem. Er arbeitet deshalb nur, wenn der freie Speicher knapp wird. Die Arbeitsweise des Garbage Collectors wird deshalb normalerweise vollständig der JVM überlassen, die einen ausgefeilten Plan hat, wann er gestartet wird und mit welcher Priorität er arbeitet.
Wenn alles so ideal geregelt ist, wo kommen dann Memory Leaks her? Die Antwort ist einfach: Wenn eine Java-Bibliothek, die vom Integration Server/Lobster_data verwendet wird, ihrerseits auf eine System Library zugreift, wird der Speicher, den diese System Library braucht, von der JVM bereitgestellt. Der Garbage Collector kann aber in diese Bibliotheksfunktionen nicht hineinsehen und hat keine Möglichkeit, toten Speicher innerhalb dieser System Library zu finden. Leider haben Betriebssysteme die Unart, eine einmal geladene System Library nie wieder zu entladen und auch die JVM kann die System Libraries, die von einer Java-Klasse verwendet wurden, erst dann wieder entladen, wenn die JVM selbst endet, also mit einem Neustart.
Startoptionen der JVM
Die reservierte Speichergröße beim Start wird durch die Option -Xms festgelegt, der Maximalwert bis zu dem die JVM vom Betriebssystem-Speicher nachfordern kann, wird durch die Option -Xmx bestimmt. Die Nachforderung erfolgt automatisch, wenn der aktuelle Wert zu etwa 90% durch Java-Objekte genutzt wird.
Wenn im Moment der Speicher-Nachforderung das Betriebssystem den geforderten Speicher nicht liefern kann, kann es zum Absturz der gesamten JVM kommen.
Weil die JVM einmal angeforderten Speicher nicht wieder an das Betriebssystem zurück gibt, muss man nach längerer Laufzeit damit rechnen, dass die tatsächliche Speichergröße am Endwert (-Xmx) ankommt. Deshalb ist es bei Servern, die mehrere Wochen ununterbrochen laufen sollen, richtig, den Startwert (-Xms) und den Endwert (-Xmx) identisch zu konfigurieren. Dann holt die JVM den festgelegten maximalen Speicher gleich beim Start, Nachforderungen treten nicht auf.
Speicherauslastung messen
Grundlagen der Messung
Innerhalb des Java Programms stehen Methoden zur Verfügung, um die Größe des Speichers zu ermitteln, den die JVM aktuell vom Betriebssystem angefordert hat und zusätzlich, wie viel davon aktuell noch frei ist. Der Wert des belegten Speichers ergibt sich als Differenz. Die Werte sind aber nur ein Augenblicks-Bild, das nach wenigen Millisekunden schon verändert ist. Um einigermaßen aussagefähige Werte zu erhalten, muss man in relativ dichter Folge die Speicherauslastung immer wieder messen und kann dann durch Mathematik den Minimalwert, den Maximalwert, den Mittelwert und die Tendenz berechnen.
Durch die selbständige Arbeitsweise des Garbage Collectors wird es aber schwierig, Memory Leaks zu finden, weil bei mittlerer Speicherauslastung noch gar nicht ernstlich aufgeräumt wurde. Wir können nicht unterscheiden zwischen dem Speicher, der noch von ungültigen Objekten belegt wird, der aber später freigeräumt wird und dem Speicher, den der Garbage Collector nicht frei räumen kann. Die Summe aller Memory Leaks könnte man nur bestimmen, wenn aktuell kein einziges "lebendes" oder "totes" Objekt im Speicher liegen würde. Ein lebendes Objekt ist dabei ein Objekt, das noch innerhalb des Programms arbeitet. Sein Speicher ist vor dem Freigeben geschützt. Ein totes Objekt wird im Programm nicht mehr gebraucht. Es liegt aber noch im Speicher, weil der Garbage Collector es noch nicht zerstört hat und seinen Speicher noch nicht wieder frei gegeben hat.
In unserem Fall ist der Minimalwert des belegten Speichers wichtig. Dieser Wert wird periodisch im Abstand einiger Sekunden immer wieder ermittelt. Daraus werden zwei Werte abgeleitet, das absolute Minimum und das "gewichtete" aktuelle Minimum. Durch gelegentlichen Start des Garbage Collectors versuchen wir möglichst wenige tote Objekte mitzumessen. Dieser Start wird bei hoher Systemlast verzögert, so dass durch den Garbage Collector kein Performance-Nachteil für den Lobster_data entsteht. Das bedeutet aber, dass wir während einer Periode hoher Systemlast keine relevanten Werte erhalten.
Startverzögerung der Messung
Systeme verhalten sich beim Start und beim Herunterfahren anders, als während des kontinuierlichen Betriebs. Zur Überbrückung der Startphase beginnt die Memory Guard Application nach dem Systemstart erst nach einer Verzögerung mit der Messung. Diese Verzögerung wird durch den Parameter delayMinutes festgelegt. Danach startet die laufende Messung alle periodeMillis Millisekunden.
Bewertung der Messwerte
Das absolute Minimum des belegten Speichers und das aktuelle Minimum werden verglichen. Der Vergleich erfolgt im Zeitraster checkMinutes. Wenn das aktuelle Minimum um mehr als die Toleranz toleranceMB über dem absoluten Minimum liegt, wird die "gelbe Linie" erreicht. In diesem Fall wird versucht, die toten Objekte frei zu geben. Dadurch wird normalerweise wieder der grüne Bereich erreicht.
Normalerweise pendelt der Status also immer zwischen "grün" und "gelb". Mit dem Konfigurations-Parameter verbose=true werden Detail-Meldungen im Control Center unter Allgemeine Meldungen angezeigt, auch der Status "grün", "gelb" oder "rot".
Bei einem belasteten System wird der Start des Garbage Collectors verzögert, jedoch nicht länger als 10 mal checkMinutes. Als belastet gilt es, wenn mehr als 1/3 des RAM belegt ist.
Wird der grüne Bereich länger als redDelayMinutes nicht wieder erreicht, geht das System in den "roten" Alarmbereich über. In diesem Fall wird ein E-Mail mit einer Alarm-Meldung an die konfigurierte Adresse gesendet. Die Sendung wird nach weiteren redDelayMinutes wiederholt, wenn der "rote" Zustand weiter besteht.
Installation und Konfiguration
Die Java-Bibliothek X_MemoryGuardApplication.jar in Verzeichnis ./extlib kopieren. In der Konfigurationsdatei ./etc/startup.xml wird folgender Eintrag eingefügt.
<
Call
name
=
"addApplication"
>
<
Arg
>
<
New
class
=
"de.lobster.memory.MemoryGuardApplication"
>
<
Set
name
=
"verbose"
type
=
"boolean"
>true</
Set
>
<
Set
name
=
"delayMinutes"
>10</
Set
>
<
Set
name
=
"redDelayMinutes"
>240</
Set
>
<
Set
name
=
"checkMinutes"
>60</
Set
>
<
Set
name
=
"toleranceMB"
>200</
Set
>
<
Set
name
=
"periodeMillis"
>60000</
Set
>
<
Set
name
=
"mailServer"
>mail.xyz.tld</
Set
>
<
Set
name
=
"mailUser"
>user@domain</
Set
>
<
Set
name
=
"mailUserPassword"
>######</
Set
>
<
Set
name
=
"mailSender"
>memoryguard@xyz.tld</
Set
>
<
Set
name
=
"mailRecipient"
>operating@xyz.tld</
Set
>
<
Set
name
=
"additionalSystemId"
>PETER_PROD</
Set
>
</
Arg
>
</
Call
>
Parameterbedeutung/Default-Werte
Name |
Bedeutung |
Default |
verbose |
Detail-Logs in Control Center/Allgemeine Meldungen. |
false |
delayMinutes |
Verzögerung nach Start bis zur ersten Messung [Minuten]. |
10 |
redDelayMinutes |
Verzögerung bis zum E-Mail-Senden [Minuten]. |
240 |
checkMinutes |
Intervall der Messwert-Auswertung. |
60 |
toleranceMB |
Erlaubte Toleranz bis "gelbe Linie" [MBytes]. |
200 |
periodeMillis |
Intervall der Messwert-Probenahme [Millisekunden]. |
60000 |
mailServer mailUser mail UserPassword |
Konfiguration des E-Mail-Servers. |
Aus startup.xml: HubStartConfiguration |
mailSender mailRecipient |
Konfiguration der Mail-Adressen. |
Aus startup.xml: DataWizardSetup |
additionalSystemId |
Systemname im E-Mail-Betreff. |
leer |
Minimal-Konfiguration, nur mit Defaults
<
Call
name
=
"addApplication"
>
<
Arg
>
<
New
class
=
"de.lobster.memory.MemoryGuardApplication"
>
</
Arg
>
</
Call
>