7 Stimmen

Delphi-String-Leck

Ich arbeite mit Delphi XE und schreibe eine Anwendung, die RemObjects SDK zur Kommunikation verwendet (falls das relevant sein sollte). Ich habe FastMM Debug eingeschaltet, und manchmal (nicht immer), wenn ich es schließe, gibt es eine Warnung über ein einzelnes "Unexpected Memory Leak". "Ein unerwartetes Speicherleck ist aufgetreten. Die unerwarteten kleinen Blocklecks sind: 117-124 Bytes: UnicodeString x 1". Sehr gelegentlich bekomme ich x2 gemeldet.

Nun, mein Verständnis ist, dass Strings Referenz gezählt werden, und da es kein anderes Objekt beteiligt, um das Leck zu verursachen, was könnte die Situation sein, die dies zu geschehen verursachen könnte? In diese StackOverflow-Frage Die Menschen finden keine Möglichkeit, ein Leck zu finden.

Wenn es keinen offensichtlichen Weg gibt, dann werde ich den neuesten FastMM-Quellcode herunterladen (er scheint nicht im XE-Quellcode enthalten zu sein).

[Die Lösung, dies zu finden, war, den FastMM-Quellcode zu installieren und den FullDebugMode zu aktivieren, um den Stack-Trace zu erhalten.

8voto

Lieven Keersmaekers Punkte 55277

Bei der Verwendung von typisierten Konstanten und in Abhängigkeit von der Finalisierungsreihenfolge war es früher möglich, dass FastMM ein Leck meldete, obwohl es in Wirklichkeit keines gab.

FastMM: Durchgesickerter Speicher wird dort gemeldet, wo er meiner Meinung nach nicht sein sollte.

Kurz gesagt, wenn die Einheit FinalizedFirst finalisiert wird, wird die SString-Konstante freigegeben. Nachdem die Finalisierung der Unit abgeschlossen ist, wird die Finalisierung von FinalizedLast aufgerufen. [ ] Finalisierung wird die Methode LeakMemory des FinalizedFirst Methode auf. Die SString-Variable wird wieder initialisiert und wird nicht freigegeben, da die Finalisierung von FinalizedFirst bereits gelaufen ist.

AbgeschlossenLetzte Einheit

unit FinalizedLast;    

interface

uses FinalizedFirst;

implementation

initialization LeakMemory;
finalization LeakMemory;
end.

AbgeschlossenErste Einheit

unit FinalizedFirst;

interface

procedure LeakMemory;

implementation

uses FinalizedLast;

procedure LeakMemory;
const
  SString: string = '';
begin
  //***** SString will get initialized once or twice depending on the
  // finalization order of units. If it get's initialized twice,
  // a memory leak is reported.
  if SString = '' then
  SString := 'FooBar';
end;
end.

Projekt LeakMemory

program LeakMemory;
uses
FastMM4 in 'FastMM4.pas',
Forms,
FinalizedFirst in 'FinalizedFirst.pas',
FinalizedLast in 'FinalizedLast.pas';

{$R *.RES}
begin

Application.Initialize;
Application.Run;

end.

6voto

Zoë Peterson Punkte 12739

Sie können Zeichenketten auslaufen lassen, wenn Sie Datensätze auf dem Heap mit FreeMem anstelle von Dispose freigeben oder wenn Sie einen Datensatz mit System.Move oder FillChar überschreiben. Im ersten Fall wird der Finalisierungscode nicht ausgeführt und im zweiten Fall, wenn das Stringfeld mit einer Null gefüllt wurde, wird angenommen, dass es bereits gelöscht wurde.

Wenn Sie den Ort des Lecks finden wollen, laden Sie FastMM herunter und aktivieren Sie FullDebugMode. Es wird ein Stack-Trace enthalten, wo das Leck aufgetreten ist.

5voto

David Heffernan Punkte 585606

Normalerweise sehe ich String-Leaks, wenn sie in anderen Objekten enthalten sind, die nicht ordnungsgemäß zerstört wurden. Zum Beispiel ein Objekt, das nicht freigegeben wurde. Sie würden jedoch erwarten, dass dieses Objekt auch gemeldet wird.

Die Lösung dieses Problems besteht darin, die Vollversion von FastMM herunterzuladen und zu verwenden und es so zu konfigurieren, dass es Stack Traces meldet, wenn es Lecks entdeckt. Wenn Sie das tun, erhalten Sie einen vollständigen Stack-Trace des Codes, der das ausgelaufene Objekt zugewiesen hat, und zu diesem Zeitpunkt ist in der Regel klar, wo das Problem liegt.

5voto

Ken Bourassa Punkte 6167

Die einzige Möglichkeit, die mir in den Sinn kommt, bei der man eine Zeichenkette auslaufen lassen kann, ohne sie absichtlich zu zerstören (z. B. durch manuelles Inkrementieren des Ref-Counts oder durch eine unordentliche Zeigeroperation), ist die Verwendung von threadvar.

Wie in der Hilfedatei angegeben,

Dynamische Variablen, die in der Regel durch den Compiler verwaltet werden (lange Strings, w Varianten und Schnittstellen) können mit threadvar deklariert werden, aber der Compiler gibt nicht automatisch die den vom Heap zugewiesenen Speicher, der von jedem Ausführungs-Thread erzeugt wird. Wenn Sie diese Datentypen in Thread-Variablen, liegt es in Ihrer Verantwortung, den ihren Speicher innerhalb des Thread zu entsorgen, bevor der Thread beendet wird.

Abgesehen davon fällt mir nichts ein, was nicht schon gesagt wurde.

[Dies war tatsächlich das Problem, und der spezifische Code lautete wie folgt:

threadvar
    g_szAuthentication : String;

procedure TMyBase.SetAuthentication(szUserName, szPassword: String);
begin
    g_szAuthentication := '?name=' + szUserName + '&pass=' + szPassword;
end;

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X