Memoryleak

Memoryleak

Speicherleck (englisch memory leak, gelegentlich auch Speicherloch oder kurz memleak) werden Fehler in einem Computerprogramm genannt, die es ermöglichen, dass ein laufender Prozess einen Speicherbereich belegt, diesen jedoch im Zuge der Ausführung weder freigeben noch nutzen kann.

Inhaltsverzeichnis

Problematik

Da unbeschränktes Wachstum der exklusiven Nutzung einer beschränkten Ressource unmöglich ist, stellt ein Speicherleck ein Problem der Software-Entwicklung dar, weil Speicher eben nur begrenzt zur Verfügung steht. Dies kann in Multitasking-Umgebungen auch andere Prozesse beeinträchtigen, die selbst kein Speicherleck aufweisen. In jedem Fall wird aber bei hinreichend langer Laufzeit das Speicherleck zu unerwünschten Erscheinungen führen. Mit dem Ende des Prozesses wird in zeitgemäßen Betriebssystemen auch sein Speicheranspruch aufgegeben.

Die Frage, ob ein Programm ein Speicherleck aufweist, ist wegen des Satzes von Rice nicht entscheidbar.

Lösungsmöglichkeiten

Das Betriebssystem ist nur sehr eingeschränkt in der Lage, Abhilfe zu schaffen, da es üblicherweise in die internen Datenstrukturen der Prozesse keine hinreichend tiefe Einsicht haben kann. Einfach zu implementieren ist die Beschränkung des Speicheranspruchs auf einen Wert, der den Ansprüchen des jeweiligen Prozesses genügen sollte, so dass im Falle eines Speicherlecks wenigstens keine anderen Prozesse beeinträchtigt werden. Es sind auch andere Ansätze denkbar, bei denen durch Heuristiken entschieden wird, welcher der Prozesse derjenige ist, der ein Speicherleck haben könnte, um eben den dann zu beenden.

Bei den möglichen Ansätzen zur Speicherverwaltung kann grundsätzlich zwischen zwei Alternativen unterschieden werden:

  1. Automatische Speicherbereinigung
  2. Explizite Speicherfreigabe

Es gibt folgende Ansätze zur Vermeidung von Speicherlecks:

  1. Analyse des Graphen aus Speicherbereichen (Knoten) und Referenzen (Kanten)
  2. Analyse des Quell-Codes ähnlich der formalen Verifikation
  3. Analyse konkreter Laufzeit-Situationen im Rahmen eines Software-Tests

Im folgenden sollen diese Alternativen kurz betrachtet werden.

Automatische Speicherbereinigung

Falls die verwendete Laufzeitumgebung eine automatische Speicherbereinigung bereitstellt, kann von einem Prozess Speicher belegt und verwendet werden. Sobald die Laufzeitumgebung feststellt, dass ein belegter Speicherbereich nicht mehr erreichbar ist, wird dieser wieder freigegeben. Nicht mehr erreichbar heißt in diesem Zusammenhang, dass keine gültige Variablenreferenz für den belegten Speicherbereich mehr existiert. Im allgemeinen Fall ist dieser Ansatz jedoch ungenügend, da fälschlich gespeicherte Referenzen nicht erkannt werden können.

Explizite Speicherfreigabe

Im Gegensatz zur automatischen Speicherbereinigung kann der Anwendungsentwickler in anderen Programmiersprachen (C, C++ oder Pascal) die Speicherverwaltung selbst implementieren. Das heißt, es können dynamisch Speicherbereiche angefordert, verwendet und anschließend wieder freigegeben werden. Die Speicherbelegung erfolgt explizit an vom Programmierer vorgesehenen Stellen und ist nicht an die Existenz einer Variablenreferenz gekoppelt, was zusätzlich zu den Schwierigkeiten der automatischen Speicherbereinigung Fehlerquellen schafft.

Systematische Tests

Durch systematisches Testen mit Hilfe entsprechender Werkzeuge, die mit einer gewissen Sicherheit feststellen können, welche Speicherbereiche einem Speicherleck zuzuordnen sind, kann man den Problemen der formalen Verifikation entgehen.

Ein bekanntes solches Werkzeug ist Valgrind. Es untersucht (in Linux-Systemen) die Speicherbenutzung eines Prozesses zur Laufzeit. In wenigen offensichtlichen Fällen kann es ausreichen, nur den Speicherverbrauch eines Prozesses im zeitlichen Verlauf zu beobachten.

Formale Verifikation

Durch einen Korrektheitsbeweis können insbesondere auch Speicherlecks entdeckt werden. Dieses Verfahren ist jedoch sehr zeitaufwendig und benötigt hochqualifiziertes Personal. Die auf Speicherlecks spezialisierte Frage kann auch computergestützt untersucht werden.

Beispiele

Das bekannteste Beispiel für fehlende automatische Speicherverwaltung ist die Sprache C. Neuer Speicher wird in C-Programmen durch die Funktion malloc angefordert. Dabei liefert malloc einen Zeiger auf den Anfang des entsprechenden Speicherbereichs. Dieser Verweis ist notwendig, um den Speicher mittels geeignetem Code wieder freizugeben (mittels der Funktion free). Geht der Zeiger verloren oder wird verändert, kann der Prozess nicht mehr auf diesen Speicher zugreifen und ihn damit auch nicht freigeben.

Beispiel für Personen ohne Programmierkenntnisse

Dieses Beispiel soll Personen ohne Programmierkenntnisse zeigen, wie ein Speicherleck entstehen kann. Es ist ein frei erdachtes Beispiel.

Das Beispiel zeigt einen Ausschnitt eines Programms in Pseudocode zur Kontrolle eines Aufzugs. Dieser Teil des Aufzugprogramms wird immer ausgeführt, wenn jemand im Aufzug einen Knopf zum Wählen einer Etage drückt.

Wenn ein Knopf gedrückt wird:
 Reserviere Speicher um die gewünschte Etagennummer zu speichern
 Schreibe die Etagennummer in den reservierten Speicher
 Befindet sich die Kabine bereits auf der gewünschten Etage?
 Wenn ja, gibt es nichts zu tun: Unterprogramm beenden
 Ansonsten:
   Warten bis der Aufzug ohne weitere Aufgaben ist
   Zu der gewünschten Etage fahren
   Den Speicher freigeben, in dem die gewünschte Etagennummer abgelegt wurde

Dieses Programm hat ein Speicherleck. Wenn eine Etage gewählt wurde und der Lift sich schon auf dieser Etage befindet, wird der Speicher, der in der zweiten Programmzeile reserviert wurde, niemals freigegeben. Jedes Mal wenn das passiert, leckt mehr Speicher.

Dies hat keinen sofortigen Effekt. Liftbenutzer drücken selten den Knopf der Etage, auf der sie sich gerade befinden, und der Lift könnte so viel freien Speicher besitzen, dass dies hunderte oder tausende von Malen ohne Probleme funktionieren würde. Jedoch könnte der Aufzug irgendwann seinen kompletten Speicher aufgebraucht haben. Dies könnte Monate oder Jahre dauern und ist deshalb auch sehr schwer durch Tests herauszufinden.

Die Konsequenzen in diesem Fall wären unangenehm. Der Lift könnte aufhören auf Benutzereingaben zu reagieren und nicht mehr weiterfahren. Falls das Programm auch Speicher benötigt, um die Türen zu öffnen, könnte jemand durch den Fehler im Lift eingesperrt werden, da kein Speicher mehr verfügbar wäre, um die Türen zu öffnen.

Das Speicherleck würde nur so lange existieren, wie das Programm läuft. Würde der Lift abgeschaltet werden, würde das Programm stoppen. Nach Reaktivierung würde das Programm neu starten, der Speicher wäre wieder freigegeben und der langsame Prozess des Speicherleckens würde erneut beginnen.

C

Das folgende Beispiel zeigt die Entstehung eines Speicherlecks anhand eines in C implementierten Programms:

int main(void)
{
    int *a, *b; /* Zeiger auf einen als Ganzzahl interpretierten Speicherbereich */

    /* Speicher für Zeiger reservieren. */
    a=malloc(sizeof(int));
    b=malloc(sizeof(int));

    *a = 5;     /* Schreibt den Wert „5“ in den von a referenzierten Speicherbereich */
    *b = 3;     /* Schreibt den Wert „3“ in den von b referenzierten Speicherbereich */
     a = b;     /* Weist dem Zeiger a das Ziel des Zeigers b zu. */

    /* a und b verweisen nun auf den gleichen Speicherbereich. Der zuvor von a referenzierte
     * Speicher ist damit verwaist und kann nicht mehr freigegeben werden. An dieser Stelle
     * entsteht ein Speicherleck.
     */

    free(b);    /* Gibt den von b referenzierten Speicher frei */
    free(a);    /* Führt zu einem Fehler, da der Bereich, auf den a
                 * verweist, bereits freigegeben wurde (in der vorherigen Zeile)
                 */
    return EXIT_SUCCESS;
}

Ab dem Schritt a = b ist es nahezu unmöglich, auf den Speicherbereich zuzugreifen, auf den a zuvor verwies. Der Bereich kann meist auch nicht mehr vom Programm freigegeben werden.

C++

Speicherlecks unter C++ entstehen genau so wie unter C. Eine weitere Fehlerquelle, die unter C nicht auftritt, ist die Verwendung des falschen delete-Operators. Wird ein Array von Objekten angefordert, muss dieses Array mit dem delete[]-Operator freigegeben werden. Der normale delete-Operator bewirkt, dass nur der Destruktor des ersten Objektes aufgerufen, aber der Speicher des gesamten Arrays freigegeben wird. Für alle anderen Objekte wird der Destruktor nicht aufgerufen. Wird im Destruktor dynamisch allokierter Speicher freigegeben (wie im Beispiel) treten Speicherlecks auf. Nur der delete[]-Operator ruft für alle Objekte des Arrays den Destruktor auf und gibt danach den Speicher des Arrays frei.

class Object
{
  int* m_value;

public:
  Object()
  {
     m_value = new int;
     *m_value = 42;
  }

  virtual ~Object()
  {
     *m_value = 0;
     delete m_value;
     m_value = NULL;
  }
};

Richtig: Speicher für 5 Objekte vom Typ int angefordert. Der Speicher wird komplett freigegeben

int * pi = new int[5];
delete [] pi;

Falsch: Nur der Destruktor des Objektes po[0] wird aufgerufen. Speicherleck, da Destruktor für po[1] bis po[4] nicht aufgerufen wird.

Object * po = new Object[5];
delete po;

Richtig: Die Destruktoren aller fünf Objekte werden aufgerufen. Der gesamte allokierte Speicher wird freigegeben.

Object * po = new Object[5];
delete [] po;

Automatische Speicherbereinigung

Das folgende Beispiel zeigt das Versagen des Ansatzes der automatischen Speicherbereinigung im Pseudocode:

PROCEDURE main
 LIST a
 LOOP
  a.INSERT(1)
  IF DATE() == START_TIME()+10000 THEN BREAK
 END
END

Man sieht hier deutlich, dass der Speicherbedarf ständig anwächst. Unter der Annahme, dass der zunehmende Speicherbedarf selbst nicht Zweck des Programms ist, handelt es sich also um ein Speicherleck, da gar kein lesender Zugriff auf die Listen-Einträge erfolgt. Dieser Fehler wäre recht leicht durch statische Analyse zu erkennen, während aber der Graph aus Referenzen und Speicherbereichen den Fehler nicht erkennen lässt.


Weblinks


Wikimedia Foundation.

Игры ⚽ Нужно решить контрольную?

Schlagen Sie auch in anderen Wörterbüchern nach:

  • Утечки памяти — Утечка памяти (англ. memory leak) процесс неконтролируемого уменьшения объёма свободной оперативной памяти (RAM) компьютера, связанный с ошибками в работающих программах, вовремя не освобождающих ненужные уже участки памяти, или с ошибками… …   Википедия

Share the article and excerpts

Direct link
Do a right-click on the link above
and select “Copy Link”