2009-07-09 5 views
5

Ich hatte diesen bösen Bug, der in der Vergangenheit verschwand, aber jetzt nach einiger Zeit kehrte es zurück.FastMM4 sagt "Der Block Header wurde beschädigt"

Ich habe zwei TSam-Objekte (abgeleitet von TPersistent) erstellt und geladen in ein TAsmJob-Objekt (abgeleitet von TObjectList).

Zur Laufzeit erstellt ein Formular eine TStringGrid und dann die AsmJob, die diese beiden SAM-Objekte erstellt (und laden Sie einige Daten von der Festplatte in jedem von ihnen). Der AsmJob wird ebenfalls dem Raster zugewiesen. Wenn das Formular gelöscht wird, kümmert sich das Grid um den AsmJob, indem er ihn freigibt, wodurch die TSam-Objekte freigegeben werden. Hier ist das Problem: Das erste Objekt wird freigegeben, ohne Probleme, aber das zweite Objekt stirbt, wenn seine geerbte Methode (in Destroy Destruktor) aufgerufen wird.

Ich verwende FreeAndNil im gesamten Programm, um die Objekte zu befreien. Die TSam-Objekte sind nicht NIL !!!!! Also, das ist der erste Versuch, die Objekte zu befreien. Sogar die Daten innerhalb der Objekte sind konsistent.

Das Rückgrat des Programms sieht wie folgt aus:

**Create:** 

Form -> StringGrid 
    -> AsmJob -> Sam1, Sam2 
StringGrid.AsmJob:= AsmJob; 


**Free:** 

Form -> StringGrid -> AsmJob -> Sam1, Sam2 

Ich verstehe wirklich nicht, wo ich versuchen, das Objekt zu verdoppeln frei oder überschreiben, nachdem es freigegeben wurde.


edit:

Einige der Fehler, die ich habe:

  • FastMM einen Fehler während eines freien Block Scan-Operation erkannt hat. FastMM hat festgestellt, dass ein Block geändert wurde, nachdem er freigegeben wurde.

  • FastMM hat während eines freien Block-Scan-Vorgangs einen Fehler festgestellt. Der Block Header wurde beschädigt.

Detail:

The current thread ID is 0x19C, and the stack trace (return addresses) leading to this error is: 
402E77 [System][@FreeMem] 
4068DC [System][@DynArrayClear] 
405E2D [System][@FinalizeArray] 
405D31 [System][@FinalizeRecord] 
40432F [System][TObject.CleanupInstance] 
404272 [System][TObject.FreeInstance] 
404641 [System][@ClassDestroy] 
4D313E [UnitSam.pas][TSam.Destroy][297] 
4042BF [System][TObject.Free] 
4149ED [SysUtils][FreeAndNil] 
4D9C0A [UnitAsmJob.pas][UnitAsmJob][TAsmJob.Destroy][180] 

Ich habe alle "debug" Optionen in der IDE aktiviert, einschließlich der "Range Check". Außerdem ist das FastMM4 auf einen extrem aggressiven Debug-Modus eingestellt. Ohne FastMM oder außerhalb des Debuggers läuft das Programm gut - aber ich weiß, dass es nicht bedeutet, dass der Bug nicht mehr da ist. Tatsächlich hat es (wahrscheinlich) für mehr als ein Jahr funktioniert, bis ich FastMM installiert habe.


edit:

Danke an alle. Nein, ich fühle, dass ich mich ein bisschen in die gute Richtung bewege.

Die Struktur des Programms ist komplizierter Ich bot nur das Rückgrat, um den ursprünglichen Beitrag klein zu halten. Aber was zum Teufel, es wurde schon größer :) Also diese TSam Objekte werden verwendet, um Daten von der Festplatte zu laden. Eine Datei in jedem Objekt. Sie machen auch etwas Verarbeitung und Datenvalidierung. Für jede dieser TSam habe ich auch ein graphisches Objekt, das auf dem Bildschirm (grafisch) die Daten anzeigt, die in den TSam-Objekten enthalten sind. Jede Zeile im TStringGrid zeigt auch die Daten in TSam, aber im Text.

Eine Frage, die ich habe: Wenn ich das Programm in kleineren Stücken brechen, um herauszufinden, wo der Fehler ist, wird der Fehler immer noch angezeigt? Oder ist es möglich, nur in dieser bestimmten Konfiguration zu erscheinen?


Antwort zu „Wie funktioniert die AsmJob zu TStringGrid zugewiesen bekommen, so dass die TStringGrid die AsmJob zerstört, können Sie uns zeigen?“

MyGrid = TStringGrid 
    public 
    AsmJob: TAsmJob; 
    end; 

dann irgendwo in der TForm.Create (die Form, die das Gitter hält), kann ich

MyGrid.AsmJob=AsmJob; 

und im Destruktor des MyGrid ich tun:

begin 
    FreeAndNil(AsmJob); 
    inherited 
end; 

Antwort

12

Dieser Fehler bedeutet, dass der Code die Strukturen des internen Speichermanagers beschädigt hat. Ihr Aufruf-Stack repräsentiert einen Punkt, wenn MM dies erkannt hat. Dies ist kein Fehlerpfad oder irgendetwas damit verbunden. Der eigentliche Fehler tritt VOR diesem Moment auf. Es kann oder kann nicht im Zusammenhang mit den genannten Klassen stehen.

You should try to use "Range check errors" option (don't forget to make Build, not Compile) and FastMM in full debug mode (with CheckHeapForCorruption, CatchUseOfFreedInterfaces и DetectMMOperationsAfterUninstall options enabled).

Sie können auch einschalten FullDebugModeScanMemoryPoolBeforeEveryOperation globale Variable, einen Fehler fast sofort zu erhalten, nachdem Problem auftritt, aber diese Option verlangsamt den A LOT Ausführung.

Wahrscheinlich ist die beste Wahl, ScanMemoryPoolForCorruptions regelmäßig aufzurufen. Nennen Sie es an einem Ort. Haben Sie einen Fehler? Nennen Sie es früher. Immer noch ein Fehler? Nennen Sie es früher wieder. Kein Fehler? Ihr Problem liegt irgendwo zwischen diesen letzten Anrufen. Jetzt können Sie die Variable FullDebugModeScanMemoryPoolBeforeEveryOperation verwenden, um einen genauen Standort zu erhalten. Schalten Sie es nur im Bereich dieses Codes ein und schalten Sie es direkt danach aus.

Es gibt einen sehr ähnlichen Fehler: "FastMM hat festgestellt, dass ein Block nach der Freigabe geändert wurde". In diesem Fall ändert Ihr Code nicht interne Strukturen, sondern anderen Speicher, der überhaupt nicht verwendet wird ("freier Speicher").

BTW, Ihr Fehler ist NICHT doppelt frei! Wenn es sich um einen doppelt freien Anruf handelt, wird FastMM dies explizit sagen (es ist leicht zu erkennen, da Sie versuchen, einen nicht benutzten oder nicht vorhandenen Speicherblock zu löschen): "Es wurde versucht, ein/eine freizugeben/neu zuzuordnen nicht zugeordneter Block ".

+0

Danke Alexander. Ich hatte keine Ahnung von "ScanMemoryPoolForCorruptions". Ich denke, eine Funktion wird von der FastMM DLL angeboten. Ich werde jetzt gleich danach suchen. – Ampere

+0

Das ist die Funktion von Standard FastMM4.pas. Es ist von der vollständigen eigenständigen Version von FastMM. Es ist nicht in der FastMM-Version vorhanden, die in Delphi integriert ist. Hier ist keine DLL in Frage. Dies ist nur eine Funktion in üblichen Pas-Datei;) – Alex

+0

Leider ist die Verbindung tot. Aber Sie können darauf zugreifen: http://web.archive.org/web/20091007162116/http://blog.eurekalog.com/?p=198 – EMBarbosa

4

A Block-Header, der beschädigt wird, bedeutet normalerweise, dass etwas den Speicher überschrieben hat, normalerweise durch eine Art unsicheren Vorgang. Verwenden Sie in Ihren Aufgaben rohe Zeiger oder Assemblercode? Wenn Sie die Bereichsüberprüfung und die Bereichsüberprüfung deaktiviert haben, versuchen Sie, sie einzuschalten und neu zu erstellen. Sie können helfen, viele dieser Art von Problemen zu fangen.

+0

Hallo Mason. Kein ASM, keine rohe Zeigeroperation, Bereichsüberprüfung ist IMMER eingeschaltet. Genau das versuche ich heute den ganzen Tag herauszufinden: Wo könnte ich das verdammte Objekt überschreiben? – Ampere

+1

Ich habe eine solche Situation schon einmal gesehen. Irgendwann habe ich es zu einer 3rd-Party-Bibliothek zurückverfolgt, die ich benutzte, die einige Operationen mit rohen Zeigern machte und einige von ihnen fummelte. Könnte das hier der Fall sein? –

+0

Übrigens sieht es nicht so aus, als ob das Objekt nicht überschrieben wird, sondern der Header des Speicherblocks. Aber um sicher zu sein, versuchen Sie, einen Haltepunkt in den Bereinigungscode zu setzen, mit dem Sie den Zustand dieses Objekts untersuchen können, bevor der Destruktor ausgeführt wird. Schau es dir im Debugger an und stelle sicher, dass seine Felder gültig sind. (Wenn nicht, dann können Sie einen Adress-Haltepunkt verwenden, um das zu finden, was Ihr Gedächtnis trivial überschreibt.) –

1

Ein paar Dinge und ich frage, weil ich Ihren Code nicht sehen kann.

mit dem folgenden Code:

procedure TForm1.FormCreate(Sender: TObject); 
var 
    wObjLst : TObjectList; 
begin 
    wObjLst := TObjectList.Create; 
    try 
     wObjlst.OwnsObjects := true; 
     wObjlst.Add(TPersistent.Create); 
     wObjlst.Add(TPersistent.Create); 
    finally 
     freeandnil(wObjlst); 
    end; 
end; 

Dies funktioniert mit Überschreitungsfehler.

Sie feststellen, dass

At runtime, a form creates a TStringGrid and then the AsmJob which creates those two SAM objects (and load some data from disk in each of them). The AsmJob is also assigned to the grid. When the form is destroyed, the Grid takes care of the AsmJob by freeing it, which frees the TSam objects. Here is the problem: the first object is freed withot problems but the second one dies when its inherited method (in Destroy destructor) is called.

Meine erste Frage ist, wie sich die AsmJob zu TStringGrid zugewiesen bekommen, so dass die TStringGrid die AsmJob zerstört, können Sie uns zeigen?

Zweitens, warum erstellen Sie einen Nachkomme von TObjectList, um es zu erhalten, um zwei Objekte zu speichern und dann befreien Sie sie, anstatt sie selbst zu erstellen und lassen Sie die TObjectList zerstören sie wie oben gezeigt.

Die andere Sache zu versuchen ist, das volle FastMM4 Paket von fastmm.sourceforge.net herunterzuladen, es zu installieren und die fulldebug dll zu verwenden, um genau herauszufinden, welches Objekt fehlschlägt. Sie und ich gehen davon aus, dass es sich um eines der SAM-Objekte handelt und möglicherweise oder auch nicht.

+0

Hallo Ryan. Ursprünglich wurde die ObjectList auf OwnObj = true festgelegt, aber jetzt löse ich die Objekte "manuell", um zu sehen, wo der Fehler auftritt. So habe ich festgestellt, dass der Fehler im Aufruf von geerbt (zerstört) der TSam-Objekte auftritt. ANYWAY! Wenn ich die TSam-Objekte manuell erstelle und benutze (also ohne TAsmJob - das ist eine Art Manager dieser Objekte), funktioniert alles wunderbar. Ich habe überhaupt keine Fehler. -------- PS: Ich habe bereits das FastMM im vollen Debug-Modus. – Ampere

2

Es könnte irgendwo im Code ein logisches Rennen sein, in das ein Objekt geschrieben wird, während es freigegeben wird. Fügen Sie NULL-Checks und andere IPC-Mechanismen (Sperrlisten usw.) hinzu, um sicherzustellen, dass dies nicht der Fall ist.

Eine weitere Option könnte darin bestehen, den Code zu untergliedern, um eine Protokollierung hinzuzufügen - und zu prüfen, ob auf Objekte sequenziell zugegriffen wird.