2009-06-30 9 views
10

Ich verwende boost :: shared_ptr in meiner Anwendung in C++. Das Speicherproblem ist wirklich ernst, und die Anwendung benötigt viel Speicher.Woher weiß ich, wer shared_ptr <> hält?

Da jedoch jedes neu eingefügte Objekt in einen shared_ptr eingefügt wird, kann beim Beenden der Anwendung kein Speicherverlust festgestellt werden.

Es muss etwas wie std::vector<shared_ptr<> > Pool mit der Ressource sein. Wie kann ich wissen, wer die shared_ptr beim Debuggen hält?

Es ist schwer, Code Zeile für Zeile zu überprüfen. Zu viel Code ...

Vielen Dank!

Antwort

20

Sie können nicht wissen, indem Sie nur auf shared_ptr schauen, wo die "Geschwister-Zeiger" sind. Sie können testen, ob einer unique() ist oder den use_count() unter other methods.

1

Sie haben offensichtlich Referenzen auf Ihre Objekte in Ihrer Anwendung. Dies bedeutet, dass Sie absichtlich die Dinge im Gedächtnis behalten. Das bedeutet, Sie haben kein Speicherleck. Ein Speicherverlust tritt auf, wenn Speicher zugewiesen wird und Sie dann keinen Verweis auf seine Adresse behalten.

Im Grunde müssen Sie Ihr Design betrachten und herausfinden, warum Sie so viele Objekte und Daten im Speicher halten und wie Sie es minimieren können.

Die eine Möglichkeit, dass Sie ein Pseudo-Memory-Leck haben, ist, dass Sie mehr Objekte erstellen, als Sie denken. Versuchen Sie, Breakpoints auf alle Anweisungen zu setzen, die ein 'new' enthalten. Sehen Sie nach, ob Ihre Anwendung mehr Objekte erstellt, als Sie denken, und lesen Sie diesen Code dann durch.

Das Problem ist wirklich nicht so sehr ein Speicherleck als ein Problem des Designs Ihrer Anwendung.

+1

Vielen Dank! Es gibt ungefähr 200 Tausende Zeilen. Es ist also schwierig, jedes neue ... zu überprüfen. Gibt es ein Compiler-Makro, um die Ref-Check-Fähigkeit von Boost zu aktivieren (wenn eine solche Fähigkeit vorhanden ist). Der Speicher verursacht durch Programmierungslogik Fehler über Pools, ich bin sicher, aber ich kann es einfach nicht finden. – user25749

+3

Sie können immer noch Speicherlecks mit shared_ptrs haben. Erstellen Sie eine zyklische Referenz und sie wird nie gelöscht, selbst wenn der Rest der App nicht mehr darauf verweist. Instant Speicherleck! – jalf

+5

Ein Verweis auf ein Objekt, das falsch beibehalten wird, ist immer noch ein Ressourcenverlust. Dies ist der Grund, warum GC-Programme immer noch Lecks haben können, normalerweise aufgrund des Observer-Musters - der Beobachter ist auf einer Liste statt der Observablen und wird nie aus ihm genommen. Letztendlich wird ein 'remove' für jedes' add' benötigt, genauso wie ein 'delete' für jedes' new' benötigt wird. Genau der gleiche Programmierfehler, der genau das gleiche Problem verursacht. Eine "Ressource" ist eigentlich nur ein Paar von Funktionen, die mit entsprechenden Argumenten gleich oft aufgerufen werden müssen, und ein "Ressourcenleck" ist das, was passiert, wenn Sie dies nicht tun. –

3

Möglicherweise tritt ein gemeinsam genutzter Zeigerspeicherverlust über Zyklen auf. Was passiert, ist, dass Ihre geteilten Objekte Referenzen auf andere geteilte Objekte enthalten, die schließlich zum Original zurückführen. Wenn dies geschieht, behält der Zyklus alle Referenzzählungen bei 1, obwohl niemand sonst auf die Objekte zugreifen kann. Die Lösung ist weak pointers.

+0

Vielen Dank! Ich benutze weak_ptr wirklich als Ressourcenbeobachter. Daher weiß ich, dass eine große Menge von shared_ptr <> im Speicher vorhanden ist. Ich bin sicher, dass es keine Zyklen gibt, irgendein Modul ist schlecht entworfen, ich versuche, es herauszufinden. – user25749

+2

"_Die Lösung ist schwache Zeiger._" Nein ist es nicht. Die Lösung besteht darin, das Design zu überprüfen. – curiousguy

+0

@curiousguy: Ja ja, um pedantisch die _real_ Lösung zu zyklischen Besitz zu sein, ist es zu vermeiden. Es gibt jedoch legitime Probleme, die besser mit zyklischen Zeigern gelöst werden können. In diesen Fällen: Eine Lösung für ein Speicherleck, die durch eine wirklich benötigte zyklische Referenz verursacht wird, ist die Verwendung von 'std :: weak_ptr'. –

3

Versuchen Sie, etwas von Ihrem Code zu refactoring, so dass die Eigentumsrechte expliziter durch die Verwendung von schwachen Zeigern anstelle von geteilten Zeigern an einigen Stellen ausgedrückt wird.

Beim Betrachten Ihrer Klassenhierarchie ist es möglich zu bestimmen, welche Klasse wirklich einen geteilten Zeiger halten soll und welche nur den schwachen braucht, damit Sie Zyklen vermeiden können, wenn es welche gibt und das "echte" Besitzerobjekt zerstört wird "Nicht-Besitzer" -Objekte sollten schon weg sein. Wenn sich herausstellt, dass einige Objekte zu früh Zeiger verlieren, müssen Sie in Ihrer App nach der Reihenfolge der Objektzerstörung suchen und diese beheben.

+0

Was Sie sagen, ist, dass ** schwache Referenzen für das Debuggen verwendet werden können **: Wenn die schwache Referenz tot ist, müssen wir sie verwenden, das bedeutet, dass das Programm einen Fehler hat. Dies ist das erste Mal, dass ich diese Idee auf OS deutlich zum Ausdruck bringe - einige andere Verwendungen von 'weak_ptr' scheinen zu implizieren, dass es ein Debugging-Tool ist, aber das sagen sie nicht klar. Anmerkung: das ist weniger effizient als die Verwendung eines regulären C++ - Zeigers: Vielleicht brauchen wir ein "checked_ptr", das als "weak_ptr" oder als "regulärer Zeiger" definiert werden kann. – curiousguy

-1

Es ist nicht möglich festzustellen, welche Objekte shared_ptr innerhalb des Programms besitzen. Wenn Sie unter Linux sind, ist eine sichere Möglichkeit, die Speicherverluste zu debuggen, das Tool Valgrind - während es Ihre Frage nicht direkt beantwortet, wird es sagen, wo der Speicher zugewiesen wurde, was normalerweise ausreicht, um das Problem zu beheben. Ich kann mir vorstellen, dass Windows vergleichbare Tools hat, aber ich weiß nicht, welcher der beste ist.

11

Die weit verbreitete Verwendung von shared_ptr wird fast zwangsläufig unerwünschte und ungesehene Speicherbelegung verursachen.

Zyklische Referenzen sind eine bekannte Ursache und einige von ihnen können indirekt und schwer zu erkennen sein, besonders in komplexem Code, der von mehr als einem Programmierer bearbeitet wird; Ein Programmierer kann entscheiden, dass ein Objekt einen Verweis auf einen anderen benötigt, um eine schnelle Lösung zu finden, und hat keine Zeit, den gesamten Code zu untersuchen, um zu sehen, ob er einen Zyklus schließt. Diese Gefahr wird stark unterschätzt.

Weniger gut verstanden ist das Problem von unveröffentlichten Referenzen. Wenn ein Objekt an viele shared_ptrs verteilt wird, wird es nicht zerstört, bis jeder von ihnen auf Null gesetzt ist oder den Gültigkeitsbereich verlässt. Es ist sehr leicht, eine dieser Referenzen zu übersehen und am Ende mit Objekten zu enden, die unsichtbar in der Erinnerung liegen und von denen du dachtest, dass du damit fertig bist.

Obwohl streng genommen sind dies keine Speicherlecks (es wird alles freigegeben werden, bevor das Programm beendet wird) sie sind genauso schädlich und schwerer zu erkennen.

Diese Probleme sind die Folgen von sinnvollen falschen Deklarationen: 1. Deklarieren Sie, was Sie wirklich als Single-Besitz als shared_ptr sein möchten. scoped_ptr wäre korrekt, aber jeder andere Verweis auf dieses Objekt muss ein roher Pointer sein, der frei gelassen werden könnte. 2. Deklarieren Sie, was Sie wirklich als passive Beobachtungsreferenz sehen möchten, als shared_ptr. weak_ptr wäre korrekt, aber dann haben Sie die Mühe, es jedes Mal in share_ptr zu konvertieren, wenn Sie es verwenden möchten.

Ich vermute, dass Ihr Projekt ein gutes Beispiel für die Art von Ärger ist, in die Sie diese Praxis bringen kann.

Wenn Sie eine speicherintensive Anwendung haben, benötigen Sie wirklich nur eine einzige Eigentümerschaft, damit Ihr Entwurf die Lebensdauer von Objekten explizit steuern kann.

Mit Einzelbesitz opObject = NULL; wird definitiv das Objekt löschen und es wird es jetzt tun.

Mit gemeinsamem Besitz spObject = NULL; ........ wer weiß? ......

+0

"Ein Programmierer kann entscheiden, ob ein Objekt einen Verweis auf einen anderen benötigt, um eine schnelle Lösung zu finden, und hat keine Zeit, den gesamten Code zu untersuchen, um zu sehen, ob er einen Zyklus schließt." . Er sollte die ** Design-Dokumente ** lesen. – curiousguy

+4

in der realen Welt haben viele Projekte kein Konzept von Design-Dokumenten :) – paulm

+3

@curiousguy Ich bevorzuge Code, um Dokumente zu entwerfen, wie Dokumente liegen können, und Code kompiliert. – M2tM

1

Ich würde vorschlagen, UMDH zu verwenden, wenn Sie auf Windows sind. Es ist ein sehr mächtiges Werkzeug. Verwenden Sie es, um Zuordnungen pro Transaktion/Zeitraum zu finden, die Sie voraussichtlich freigeben, und finden Sie dann heraus, wer sie hält.

Es gibt weitere Informationen zu diesem SO Find memory leaks caused by smart pointers

5

Eine Lösung beantworten baumeln oder Kreis Smart Pointer Referenzen wir getan haben, um die Smart-Pointer-Klasse anpassen, um eine Debug-only Buchhaltung Funktion hinzuzufügen. Immer wenn ein Intelligenter Zeiger eine Referenz auf ein Objekt hinzufügt, dauert es eine Stapelablaufverfolgung und legt sie in einer Karte, die jeder Eintrag verfolgt

  1. die Adresse des Objekt zugeordnet ist (was der Zeiger auf)
  2. die Adressen der einzelnen Intelligenter Zeiger
  3. die entsprechenden stacktraces aus, wenn jeder Intelligenter Zeiger konstruiert
  4. einen Verweis auf das Objekt Objekthalte

wenn ein Intelligenter Zeiger geht aus Rahmen, dessen Eintrag in der Karte wird gelöscht. Wenn der letzte Smartpointer zu einem Objekt zerstört wird, wird das Objekt in der Karte entfernt.

Dann haben wir einen "track leaks" -Befehl mit zwei Funktionen: '[re] Start Leck Tracking' (die die gesamte Karte löscht und Tracking aktiviert, wenn es nicht bereits ist), 'offene Referenzen drucken', die alle zeigt Ausstehende Smartpointer-Referenzen, die seit dem Start der Leckverfolgung erstellt wurden. Da Sie die Stapelspuren sehen können, aus denen diese intelligenten Zeiger entstanden sind, können Sie leicht wissen, wer von Ihrem Objekt befreit ist. Es verlangsamt die Dinge wenn es an ist, also lassen wir es nicht die ganze Zeit.

Es ist eine Menge Arbeit zu implementieren, aber auf jeden Fall lohnt es sich, wenn Sie eine Codebasis haben, wo dies viel passiert.

Verwandte Themen