Was soll passieren, wenn ein Objekt nach FreeAndNil verwendet wird?

8

In meinem Delphi7 ist dieser Code

%Vor%

erzeugt eine AV: Zugriffsverletzung bei der Adresse 0041D6D1 im Modul 'Project1.exe'. Lesen der Adresse 00000000. Aber jemand besteht darauf, dass es keine Ausnahme machen sollte, egal was passiert. Er sagt auch, dass sein Delphi 5 tatsächlich keine Ausnahmen macht. Er nennt dies einen "abgestandenen Zeiger Bug". Mit anderen Worten sagt er, dass FreeAndNil nicht als Debugger verwendet werden kann, um einen doppelten Versuch zu erkennen, ein Objekt freizugeben oder ein freigegebenes Objekt zu verwenden.

Kann mich jemand aufklären? Sollte dieser Raise und Fehler (immer / zufällig) oder das Programm diesen Bug ohne Probleme durchlaufen?

Danke

Ich frage das, weil ich glaube, dass ich in meinem Programm einen "doppelten freien Gegenstand" oder "freien und Wiederzugriff" -Bug habe. Wie kann ich den Speicher, der einem Objekt zugewiesen ist, mit Nullen füllen, NACHDEM ich das Objekt freigegeben habe? Ich möchte auf diese Weise erkennen, wo der Fehler liegt, indem ich AV bekomme. Anfangs hoffte ich, dass ich, wenn ich das Objekt auf FreeAndNil setze, IMMER einen AV bekommen werde, wenn ich versuche, wieder darauf zuzugreifen.

    
Rob Kennedy 12.12.2008, 21:21
quelle

6 Antworten

21

Es ist immer falsch, Methoden oder Eigenschaften eines Nullverweises zu verwenden, auch wenn es manchmal scheint, dass es funktioniert.

FreeAndNil kann tatsächlich nicht verwendet werden, um doppelte Frees zu erkennen. Es ist sicher, FreeAndNil für eine Variable namens night-nil aufzurufen. Da es sicher ist, hilft es Ihnen nichts zu erkennen.

Dies ist kein veralteter Pointer-Bug. Dies ist ein Null-Referenz-Fehler. Ein Veraltete-Zeiger-Fehler tritt auf, wenn Sie ein Objekt freigegeben haben, aber nicht alle Variablen gelöscht hat, auf die verwiesen wurde. Dann enthält die Variable immer noch die alte Adresse des Objekts. Diese sind sehr schwer zu erkennen. Sie können einen solchen Fehler erhalten:

%Vor%

Sie können auch eine solche erhalten:

%Vor%

Die Verwendung von MStr.Size , nachdem Sie das Objekt MStr referenziert haben, ist ein Fehler und sollte eine Ausnahme auslösen. Ob eine Ausnahme auslöst, hängt von der Implementierung ab. Vielleicht wird es das und vielleicht auch nicht. Es ist jedoch nicht zufällig.

Wenn Sie nach einem doppelt-freien Fehler suchen, können Sie die von FastMM bereitgestellten Debug-Hilfsprogramme verwenden, wie auch andere vorgeschlagen haben. Es funktioniert, indem es den Speicher tatsächlich nicht zurück an das Betriebssystem oder sogar zurück an den internen Freispeicherpool von Delphi freigibt. Stattdessen werden bekannte Daten in den Speicherbereich des Objekts geschrieben. Wenn Sie diese Werte sehen, wissen Sie, dass Sie etwas lesen, das Sie bereits freigegeben haben. Außerdem wird die VMT des Objekts geändert, sodass Sie beim nächsten Aufruf einer virtuellen Methode für diese Objektreferenz eine vorhersehbare Ausnahme erhalten und Ihnen sogar mitteilen können, welches vermeintlich freigegebene Objekt Sie verwenden wollten. Wenn Sie versuchen, das Objekt erneut freizugeben, kann es Ihnen nicht nur mitteilen, dass Sie es bereits freigegeben haben, sondern auch, wo es beim ersten Mal (mit einem Stack-Trace) freigegeben wurde und wo es zugewiesen wurde. Es sammelt auch diese Informationen, um über Speicherlecks zu berichten, bei denen Sie ein Objekt weniger als einmal statt mehr freigegeben haben.

Es gibt auch Gewohnheiten, die Sie verwenden können, um das Problem für zukünftigen Code zu vermeiden:

  • Verringern Sie die Verwendung globaler Variablen. Eine globale Variable kann im gesamten Programm durch einen beliebigen Code geändert werden, so dass Sie sich fragen müssen, wann Sie sie verwenden: "Ist der Wert dieser Variablen noch gültig oder hat ein anderer Code sie bereits freigegeben?" Wenn Sie den Gültigkeitsbereich einer Variablen begrenzen, reduzieren Sie die Menge an Code, die Sie in Ihrem Programm berücksichtigen müssen, wenn Sie nach Gründen suchen, aus denen eine Variable nicht den erwarteten Wert hat.
  • Machen Sie sich klar, wer ein Objekt besitzt. Wenn zwei Codeabschnitte Zugriff auf dasselbe Objekt haben, müssen Sie wissen, welcher dieser Codeabschnitte das Objekt besitzt. Sie könnten jeweils eine andere Variable zum Verweisen auf das Objekt haben, aber da ist immer noch nur ein Objekt. Wenn ein Codecode FreeAndNil für seine Variable aufruft, bleibt die Variable des anderen Codes unverändert. Wenn der andere Code denkt, dass er das Objekt besitzt, dann sind Sie in Schwierigkeiten. (Dieses Konzept des Besitzers ist nicht notwendigerweise mit der Eigenschaft TComponent.Owner verbunden. Es muss kein Objekt sein , das es besitzt; es könnte ein allgemeines Subsystem Ihres Programms sein.)
  • Behalten Sie keine dauerhaften Verweise auf Objekte, die Sie nicht besitzen. Wenn Sie keine langlebigen Verweise auf ein Objekt beibehalten, müssen Sie sich keine Gedanken darüber machen, ob diese Verweise noch gültig sind. Die einzige persistente Referenz sollte im Code enthalten sein, der das Objekt besitzt. Jeder andere Code, der dieses Objekt verwenden muss, sollte eine Referenz als Eingabeparameter erhalten, das Objekt verwenden und dann die Referenz verwerfen, wenn sie das Ergebnis zurückgibt.
Rob Kennedy 12.12.2008 21:42
quelle
9

Von dem, was ich sehe, sollte dieser Code immer zu einem Fehler führen. FreeAndNil legt diesen übergebenen Wert explizit auf Nil (alias 0) fest. Sie sollten also unbedingt eine Zugriffsverletzung erhalten, wenn Sie versuchen, das Objekt zu dereferenzieren.

    
Scott W 12.12.2008 21:38
quelle
8

Nur um das Problem zu verkomplizieren:

Wenn die Methode, die Sie aufrufen, eine statische (nicht virtuelle) Methode ist und keine virtuellen Methoden selbst aufruft oder auf Felder des Objekts zugreift, erhalten Sie auch dann keine Zugriffsverletzung, wenn die Objektreferenz gesetzt wurde zu NIL.

Der Grund dafür ist, dass die Zugriffsverletzung durch Dereferenzieren des Selbstzeigers (in diesem Fall NIL) verursacht wird, aber nur beim Zugriff auf ein Feld oder die VMT des Objekts zum Aufruf einer virtuellen Methode.

Dies ist nur eine Ausnahme von der Regel, dass Sie Methoden eines NIL-Objektverweises, die ich hier erwähnen möchte, nicht aufrufen können.

    
Thomas Mueller 12.12.2008 22:15
quelle
5

Wenn Sie einen Zeiger auf Null setzen, sollten Sie ihn nicht mehr verwenden können. Aber wenn Sie einen anderen Zeiger auf das gleiche Objekt haben, können Sie es verwenden, ohne ein AV zu bekommen, weil dieser Zeiger immer noch auf die Objektadresse zeigt und nicht auf Null.

Darüber hinaus wird durch das Freigeben eines Objekts der vom Objekt verwendete Speicher nicht gelöscht. Es markiert nur, dass es nicht verwendet wird. Das ist der Grund, warum Sie ein AV bekommen möchten. Wenn der freigegebene Speicher für ein anderes Objekt reserviert wird, erhalten Sie ein AV, weil es keine Daten mehr enthält, die gültig scheinen.

FastMM4 verfügt über einige Einstellungen, die Sie beim Debuggen verwenden können, um solche Bedingungen zu erkennen. Von der FsatMM4Options.inc:

  

{Legen Sie die folgende Option fest, um eine umfassende Überprüfung aller Speicherblöcke durchzuführen. Alle    Blöcke werden sowohl mit einem Header als auch mit einem Trailer aufgefüllt, die zur Überprüfung der    Integrität des Heaps. Freed Blöcke sind auch gelöscht, um sicherzustellen, dass sie    kann nach der Freigabe nicht wiederverwendet werden. Diese Option verlangsamt Speichervorgänge    dramatisch und sollte nur verwendet werden, um eine Anwendung zu debuggen, die ist    Überschreiben von Speicher oder Wiederverwenden von freigegebenen Zeigern. Einstellung dieser Option    aktiviert automatisch CheckHeapForCorruption und deaktiviert ASMVersion.    Sehr wichtig: Wenn Sie diese Option aktivieren, benötigt Ihre Anwendung die    FastMM_FullDebugMode.dll-Bibliothek. Wenn diese Bibliothek nicht verfügbar ist, werden Sie    beim Start einen Fehler bekommen.}
  {$ definiere FullDebugMode}

Noch ein Zitat aus der gleichen Datei:

  

FastMM fängt immer Versuche auf, denselben Speicherblock zweimal zu löschen ...

Da Delphi FastMM von Delphi 2007 (2006?) verwendet, sollten Sie einen Fehler erhalten, wenn Sie versuchen, ein Objekt doppelt zu laden.

    
Vegar 12.12.2008 21:48
quelle
1

Thomas Müller : Haben Sie virtuelle Klassenmethoden ausprobiert? Ein Konstruktor ist eine Art virtuelle Methode, aber Sie nennen sie gegen den Typ - nicht die Instanz. Dies bedeutet, dass selbst einige spezifische virtuelle Methoden nicht zu einer Nullreferenz von AV führen: D

Vegar : Du könntest nicht richtiger sein! FastMM ist das beste Werkzeug aller Zeiten, das mir geholfen hat, diese Art von Bugs aufzuspüren.

    
Matthias Hryniszak 30.12.2008 21:00
quelle
1

Der EurekaLog Blog hatte im April 2009 einen großartigen Beitrag hierzu:

Warum sollten Sie immer FreeAndNil anstelle von Free verwenden?

    
Mick 28.01.2010 19:13
quelle

Tags und Links