Funktionsprinzip von Speicherleckdetektoren

Lesezeit: 8 Minuten

Benutzeravatar von amit1990
amit1990

Wie funktionieren Speicherleckdetektoren eigentlich? Was sind die zugrunde liegenden Konzepte im Allgemeinen? Kann C++ als Sprache nehmen, um dies zu erklären.

  • Bisher wurden die (Adress-)Desinfektionsmittel nicht erwähnt. Dieses Gespräch von CppCon14 gibt eine gute Zusammenfassung, wie sie funktionieren.

    – Niemand bewegt sich von SE weg

    11. Februar 2015 um 9:30 Uhr

  • Dies ist eine Frage mit einer nützlichen und genauen Antwort. Wenn Sie es schließen oder löschen, verlieren wir nützliche Informationen. meta.stackoverflow.com/questions/287899/…

    – Martin York

    13. März 2015 um 16:45 Uhr

  • @LokiAstari: Durch das Löschen würden Informationen verloren gehen; Schließen würde es nicht.

    – John Saunders

    14. März 2015 um 3:00 Uhr

  • Das Schließen würde Personen bestrafen (indem sie ihrer Reputation beraubt werden), die Zeit damit verbracht haben, nützliche Antworten zu verfassen. Wir sollten gut geschriebene, nützliche Antworten wie die hier vorliegenden fördern.

    – jwfear

    14. März 2015 um 20:21 Uhr


Benutzeravatar von b4hand
b4hand

Es gibt ein paar verschiedene Möglichkeiten, wie Lecksucher funktionieren. Sie können die Implementierung von ersetzen malloc und free mit solchen, die während der Zuordnung mehr Informationen verfolgen können und sich nicht um die Leistung kümmern. Dies ist ähnlich wie dmalloc funktioniert. Im Allgemeinen jede Adresse, die ist malloc‘ed aber nicht free‘d ist durchgesickert.

Die grundlegende Implementierung ist eigentlich ziemlich einfach. Sie pflegen einfach eine Nachschlagetabelle für jede Zuordnung und ihre Zeilennummer und entfernen den Eintrag, wenn er freigegeben wird. Wenn das Programm fertig ist, können Sie alle Speicherlecks auflisten. Der schwierige Teil besteht darin, zu bestimmen, wann und wo die Zuordnung hätte freigegeben werden sollen. Dies ist noch schwieriger, wenn mehrere Zeiger auf dieselbe Adresse vorhanden sind.

In der Praxis möchten Sie wahrscheinlich mehr als nur die einzelne Zeilennummer, sondern eher einen Stack-Trace für die verlorenen Zuordnungen.

Ein weiterer Ansatz ist das Wie Valgrind arbeitet, das eine vollständige virtuelle Maschine implementiert, um Adressen und Speicherreferenzen und die zugehörige Buchhaltung zu verfolgen. Der Valgrind-Ansatz ist viel teurer, aber auch viel effektiver, da er Sie auch über andere Arten von Speicherfehlern wie Lese- oder Schreibvorgänge außerhalb der Grenzen informieren kann.

Valgrind instrumentiert im Wesentlichen die zugrunde liegenden Anweisungen und kann nachverfolgen, wann eine bestimmte Speicheradresse keine Referenzen mehr hat. Es kann dies tun, indem es die Zuweisung von Adressen verfolgt, und Ihnen so nicht nur sagen, dass ein Teil der Erinnerung verloren gegangen ist, sondern genau Wenn es ging verloren.

C++ macht es für beide Arten von Lecksuchern etwas schwieriger, weil es die new und delete Betreiber. Technisch new kann eine ganz andere Quelle der Erinnerung sein als malloc. In der Praxis werden jedoch viele echte C++-Implementierungen einfach verwendet malloc implementieren new oder haben eine Option zur Verwendung malloc anstelle des alternativen Ansatzes.

Auch höhere Programmiersprachen wie C++ haben tendenziell alternative Möglichkeiten zur Speicherzuweisung auf höherer Ebene, wie z std::vector oder std::list. Ein einfacher Leckdetektor würde die potenziell vielen Zuordnungen, die von den Modi höherer Ebene vorgenommen werden, separat melden. Das ist viel weniger nützlich, als zu sagen, dass der gesamte Container verloren gegangen ist.

  • Danke @b4hand für die beschreibende Antwort. Es wird also keine leichte Aufgabe sein, einen grundlegenden Speicherleckdetektor so zu implementieren, wie ich es verstehe. Und einen Zweifel habe ich aus Ihrer Erklärung. Sie sagten: “Technisch neu kann eine ganz andere Speicherquelle sein als malloc.”. In allen Szenarien sollte nicht Neu Verwenden Sie den gleichen Haufen wie malloc Verwendet?

    – amit1990

    11. Februar 2015 um 5:59 Uhr

  • Der C++-Standard legt das nicht fest, aber wie gesagt, in der Praxis verwenden die meisten realen Implementierungen dieselbe zugrunde liegende Ressource.

    – b4hand

    11. Februar 2015 um 6:03 Uhr

  • @ amit1990: Es ist plausibel, dass eine C ++ – Implementierung Speicher in großen Blöcken mit malloc zuweist und dann implementiert new um eine Art Objektpool innerhalb dieses Blocks zu verwenden. Dies kann dazu dienen, die Speicherfragmentierung zu reduzieren, da Objekte desselben Typs normalerweise dieselbe Größe haben. Für einen Speicherdebugger, der nur malloc abfängt, wird der Pool undurchsichtig.

    – Lüge Ryan

    11. Februar 2015 um 10:08 Uhr

  • Vergessen wir auch nicht, dass eine App Speicher auch direkt über Betriebssystem-APIs zuweisen kann, anstatt sie zu verwenden malloc/newund auch, dass STL-Container benutzerdefinierte Speicherzuweisungen unterstützen (so dass eine App jeden gewünschten Speicher verwenden kann – Stapelspeicher, eine speicherzugeordnete Datei usw.).

    – Rémy Lebeau

    11. Februar 2015 um 21:58 Uhr

  • @amit1990: Eigentlich was new und malloc tun sind zwei verschiedene Dinge. malloc weist einen Teil von Bytes zu, während new weist einen Teil von Bytes zu, um ein Objekt eines bestimmten Typs zu speichern UND initialisiert das Objekt, indem es seinen Konstruktor aufruft. Sie müssen nicht aus demselben Pool zuordnen, und sie müssen nicht aus einem anderen Pool zuordnen. Sie können also beides nicht vermuten. Um das Thema noch zu vervollständigen, Rationale Purify kümmert sich auch um die Erkennung malloc/free, new/delete und new[]/delete[] Fehlanpassungen. Sie instrumentieren SOs, um Speicherverwaltungsaufrufe an ihre eigenen Bibliotheken umzuleiten.

    – Laurent LA RIZZA

    12. Februar 2015 um 8:13 Uhr

Benutzeravatar von Ira Baxter
Ira Baxter

Hier ist ein veröffentlichtes technisches Papier über die Funktionsweise unseres CheckPointer-Tools.

Grundsätzlich verfolgt es die Lebensdauer aller Werte (Heap und Stack) und ihre Größe entsprechend ihren Typen, wie sie von der Sprache definiert werden. Dadurch kann CheckPointer nicht nur Lecks finden, sondern auch Zugriffe außerhalb des Arrays, selbst für Arrays im Stack, was Valgrind nicht kann.

Insbesondere analysiert es den Quellcode, um alle Verwendungen von Zeigern zu finden. (Dies ist eine ziemliche Aufgabe für sich).

Es verfolgt Zeigermetadaten für jeden Zeiger, bestehend aus

  • Ein Verweis auf die Objektmetadaten für das Heap-zugeordnete Objekt oder die globale oder lokale Variable oder Funktion, auf die der Zeiger und zeigt
  • Der Adressbereich des (Unter-)Objekts des Objekts, auf das der Zeiger aktuell zugreifen darf. Dieser kann kleiner sein als der Adressbereich des gesamten Objekts; Wenn Sie beispielsweise die Adresse eines Strukturmitglieds nehmen, erlaubt der instrumentierte Quellcode nur dann den Zugriff auf dieses Mitglied, wenn der resultierende Zeiger verwendet wird.

Es verfolgt auch die Art und den Ort jedes Objekts, dh ob es sich um eine Funktion, eine globale, Thread-lokale oder lokale Variable, einen vom Heap zugewiesenen Speicher oder eine String-Literal-Konstante handelt:

  • Der Adressbereich des Objekts, auf das sicher zugegriffen werden kann, und
  • Für jeden Zeiger, der in dem Heap-zugeordneten Objekt oder der Variable gespeichert ist, ein Verweis auf die Zeigermetadaten für diesen Zeiger.

All dieses Verfolgen wird erreicht, indem die ursprüngliche Programmquelle in ein Programm transformiert wird, das das tut, was das ursprüngliche Programm tut, und verschiedene Routinen zum Prüfen oder Aktualisieren von Metadaten verschachtelt. Das resultierende Programm wird kompiliert und ausgeführt. Wenn eine Metadatenprüfung zur Laufzeit fehlschlägt, wird ein Backtrace mit einem Bericht über die Art des Fehlers bereitgestellt (ungültiger Zeiger, Zeiger außerhalb gültiger Grenzen, …).

  • Abgewertet, weil die Antwort, wenn der Link runtergehen sollte, nicht wirklich informativ wäre.

    – dhein

    11. Februar 2015 um 9:11 Uhr

  • Die Link-Site existiert seit mehr als 10 Jahren länger als StackOverflow; es ist nicht wahrscheinlich, dass es verschwindet. Und der Link führt zu a wirklich informative Antwort. Der Zweck des Webs und der Hyperlinks besteht darin, zu vermeiden, dass das gesamte Web in Stackoverflow kopiert wird. Holen Sie sich eine Perspektive, Leute.

    – Ira Baxter

    11. Februar 2015 um 11:41 Uhr


  • Ich stimme zu, dass der Link zu einer guten Antwort führt. Aber der Punkt ist: Ohne den Link ist es nicht so nützlich. Und die Regel darüber ist klar. (ohne Perspektiven). Was hindert Sie also daran, die im Link enthaltenen Informationen einfach etwas ausführlicher zusammenzufassen?

    – dhein

    11. Februar 2015 um 12:13 Uhr

  • Ich habe mehr Details hinzugefügt. Ich beobachte, dass die Frage, „wie viel Detailtiefe“ notwendig ist, eine Funktion des Lesers ist; man kann nicht alle zufriedenstellen. Also ein Link zu “dem Rest des Details”.

    – Ira Baxter

    11. Februar 2015 um 20:18 Uhr


  • @Zaibis ohne den Link, den Sie noch zu Google “Ira Baxter CheckPointer” oder “Semantic Designs CheckPointer” kennen können

    – Benutzer253751

    11. Februar 2015 um 23:33 Uhr

Dies ist mit C und C++ gekennzeichnet und es wird kein Betriebssystem erwähnt. Diese Antwort ist für Windows.

C

Windows hat das Konzept des virtuellen Speichers. Jeder Speicher, den ein Prozess erhalten kann, ist virtueller Speicher. Dies geschieht durch VirtualAlloc() [MSDN]. Sie können sich vorstellen, dass der Lecksucher einen Haltepunkt für diese Funktion setzt, und wann immer er aufgerufen wird, erhält er den Callstack und speichert ihn irgendwo. Dann kann es ähnlich für tun VirtualFree()[MSDN].

Der Unterschied kann dann identifiziert und zusammen mit den gespeicherten Callstacks angezeigt werden.

C++

C++ hat ein anderes Konzept: Es nimmt die großen 64-kb-Blöcke, die es von VirtualAlloc() erhält, und teilt sie in kleinere Teile auf, die Heap genannt werden. Der C++ Heap Manager kommt von Microsoft und bietet neue Methoden HeapAlloc() [MSDN] und HeapFree()[MSDN].

Dann könnten Sie dasselbe tun wie zuvor, aber eigentlich ist diese Funktion bereits integriert. Microsofts GFlags [MSDN] Tool kann das Tracking aktivieren:

Screenshot: GFlags für Notepad aktiviert

In diesem Fall werden bis zu 50 MB Callstack-Informationen für C++-Heap-Manager-Aufrufe gespeichert.

Da diese Einstellungen auch über die Windows-Registrierung aktiviert werden können, kann ein Speicherleckdetektor leicht davon Gebrauch machen.

Allgemeines Konzept

Wie Sie sehen können, besteht das allgemeine Konzept darin, Zuweisungen und Freigaben zu verfolgen, sie zu vergleichen und die Callstacks der Differenz anzuzeigen.

  • Die Details darüber, wie Speicher vom Betriebssystem zugewiesen wird, sind implementierungsabhängig. Die guten Informationen hier gelten wahrscheinlich nur für Microsofts Compiler und nur mit den aktuellen Standard-Compiler-Flags. AFAICT, weder der C- noch der C++-Standard legt fest, wie Speicher vom Betriebssystem zugewiesen werden soll. Seien Sie also vorsichtig, wenn Sie dies in einem Programm verwenden müssen, das portabel sein muss.

    – Lüge Ryan

    11. Februar 2015 um 23:18 Uhr


  • 1. Sie sind nicht verpflichtet, VirtualAlloc in C zu verwenden. Es gibt immer noch malloc und free die die meisten Programme verwenden (in der Tat ist die direkte Verwendung von VirtualAlloc selten). 2. Der C++-Speichermanager ist dasselbe. 3. HeapAllocet al. sind nicht C++-spezifisch, können aber in C verwendet werden (und sind wahrscheinlich wie malloc ist implementiert).

    – Benutzer253751

    11. Februar 2015 um 23:34 Uhr

  • Plus 9?? Mann, schuld sind meine Augen, ich kann +9 einfach nicht sehen

    – Bhargav Rao

    12. März 2015 um 18:42 Uhr

1410040cookie-checkFunktionsprinzip von Speicherleckdetektoren

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy