Delphi-PRAXiS
Seite 1 von 2  1 2      

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Programmieren allgemein (https://www.delphipraxis.net/40-programmieren-allgemein/)
-   -   Delphi String freigeben nur wo? (https://www.delphipraxis.net/199757-string-freigeben-nur-wo.html)

EWeiss 18. Feb 2019 07:59


String freigeben nur wo?
 
Mein Problem ist leider immer noch nicht gelöst. :oops:

EurekaLog!
Zitat:

--------------------------------------------------------------------------------------------------------------------------------------
|Methods |Details|Stack |Address |Module |Offset |Unit |Class |Procedure/Method |Line |
--------------------------------------------------------------------------------------------------------------------------------------
|+Leak #1: Type=UnicodeString: Ref count - 1, Content: "'WARNING: DO NOT CHANGE THE ORDER OF THE PROPERTIES!"; Total size=118; Count=1|
|------------------------------------------------------------------------------------------------------------------------------------|
|00000002|03 |00000000|005E8586|SK_Aero.dll |00008586|System | |InternalUStrFromPCharLen | |
|00000002|04 |00000000|0082C55B|SK_Aero.dll |0024C55B|uSkinConfig|TSkinConfig|FBuffin |976[12] |
|00000002|04 |00000000|0082ABEF|SK_Aero.dll |0024ABEF|uSkinConfig|TSkinConfig|GetConfiguration |308[5] |
|00000002|04 |00000000|008234C4|SK_Aero.dll |002434C4|uSkin |TSkinEngine|InitSkin |644[25] |
|00000002|04 |00000000|00833E1B|SK_Aero.dll |00253E1B|uMaster | |SKAERO_InitSkin |806[4] |
|00000002|03 |00000000|75213366|kernel32.dll|00013366|kernel32 | |BaseThreadInitThunk | |
|00000002|03 |00000000|77019900|ntdll.dll |00039900|ntdll | | (possible RtlInitializeExceptionChain+97)| |
|00000002|03 |00000000|770198CE|ntdll.dll |000398CE|ntdll | | (possible RtlInitializeExceptionChain+47)| |
|------------------------------------------------------------------------------------------------------------------------------------|
Das Problem liegt hier.
FBuffin

Wenn ich im Dialog von EurekaLog doppelkicke dann geht er mir in diese Zeile.
Delphi-Quellcode:
  try
    try
      while not eof(ParseFile) do
      begin
        ReadLN(ParseFile, sBuffer);
        AppendToLinkedList(nReading, sBuffer); // sBuffer soll einen Memoryleak produzieren.
        inc(nReading);
      end;
    except
      raise Exception.Create(SysErrorMessage(GetLastError));
    end;
  finally
    nReading := 0;
    CloseFile(ParseFile);
  end;
Das Problem ist nur, ich weis nicht wo ich ihn freigeben soll.
Theoretisch würde ich es so machen.
Delphi-Quellcode:
      while not eof(ParseFile) do
      begin
        ReadLN(ParseFile, sBuffer);
        AppendToLinkedList(nReading, sBuffer); // sBuffer soll einen Memoryleak produzieren.
        inc(nReading);
        // String freigeben nachdem er übergeben wurde
        ZeroMemory(Pointer(sBuffer), Length(sBuffer) * SizeOf(Char));
        sBuffer := '';
      end;
Nur dann lösche ich den String im Array dieser ist dann auch '' obwohl ich den String erst nach der Übergabe lösche.
Delphi-Quellcode:
procedure AppendToLinkedList(nReading: Integer; sBuffer: string);
begin

  New(FPBuffer);

  if nReading = 0 then
  Begin
    New(FToPBuffer);
    LineStart := FToPBuffer;
    LineStart^.Nr := 0;
  end;

  FPBuffer^.Nr := nReading;
  FPBuffer^.Str := sBuffer; //<< der ist dann nachdem löschen von sBuffer leer
  LineStart^.Max := FPBuffer^.Nr;
  FToPBuffer^.Ptr := FPBuffer;
  FToPBuffer := FPBuffer;
end;
Keine Ahnung wo ich den String sonst freigeben soll wenn nicht an angezeigter Position.

Edit:
Hier das gleiche!
Delphi-Quellcode:
procedure FormatINI(Filename: string);
var
  sBuffer: string;
  ParseFile: TextFile;
  StringList: TStringList;
  StringParse: string;

procedure AppendToLinkedList(nReading: Integer; sBuffer: string);
begin

  New(FPBuffer);

  if nReading = 0 then
  Begin
    New(FToPBuffer);
    LineStart := FToPBuffer;
    LineStart^.Nr := 0;
  end;

  FPBuffer^.Nr := nReading;
  FPBuffer^.Str := sBuffer;
  LineStart^.Max := FPBuffer^.Nr;
  FToPBuffer^.Ptr := FPBuffer;
  FToPBuffer := FPBuffer;
end;

begin
  StringList := TStringList.Create;

  Assignfile(ParseFile, Filename);
  reset(ParseFile);

  try
    try
      while not eof(ParseFile) do
      begin
        ReadLN(ParseFile, sBuffer);
        AppendToLinkedList(nReading, sBuffer);
        inc(nReading);
      end;
    except
      raise Exception.Create(SysErrorMessage(GetLastError));
    end;
  finally
    nReading := 0;
    CloseFile(ParseFile);
  end;

  FPBuffer := LineStart;
  while (FPBuffer.Nr <= LineStart.Max - 1) do
  begin
    FPBuffer := FPBuffer.Ptr;
    StringParse := LeftTrim(FPBuffer.Str);
    if Length(StringParse) <> 0 then
      StringList.Add(FPBuffer^.Str);

    if FPBuffer.Nr > 0 then
      if LeftStr(FPBuffer^.Str, 1) = '[' then
        StringList.Insert(StringList.IndexOf(StringParse), '');

    ZeroMemory(PWideChar(FPBuffer.Str), Length(FPBuffer.Str) * SizeOf(Char));
    FPBuffer.Str := '';
  end;

  StringList.SaveToFile(Filename);
  StringList.Free;

  Dispose(FPBuffer);
  FPBuffer := nil;
  LineStart := nil;

end;
siehe Shot.. Doppelklick auf FormatINI dann springt er in diese Zeile.
Delphi-Quellcode:
AppendToLinkedList(nReading, sBuffer);

gruss

Uwe Raabe 18. Feb 2019 08:35

AW: String freigeben nur wo?
 
Das ZeroMemory ist hier wohl eher schädlich, da es die Referenzzählung der Strings umgeht. Ein Finalize(FPBuffer^) sollte da verträglicher sein.

Was mir auffällt ist, daß für jede Zeile im AppendToLinkedList ein New() aufgerufen wird, es aber nur ein einziges Dispose() gibt.

Außerdem vermisse ich die Initialisierung von nReading mit 0.

EWeiss 18. Feb 2019 08:42

AW: String freigeben nur wo?
 
Zitat:

Zitat von Uwe Raabe (Beitrag 1425839)
Das ZeroMemory ist hier wohl eher schädlich, da es die Referenzzählung der Strings umgeht. Ein Finalize(FPBuffer^) sollte da verträglicher sein.

Was mir auffällt ist, daß für jede Zeile im AppendToLinkedList ein New() aufgerufen wird, es aber nur ein einziges Dispose() gibt.

Könntest du mir bitte an Hand meines Codes das korrigieren wenn du zeit dazu findest?

Zitat:

Außerdem vermisse ich die Initialisierung von nReading mit 0.
Delphi-Quellcode:
FPBuffer^.Nr := nReading;


Nun das ist schon gegeben..
nReading ist 0 beim start.
Aber ich kann es noch ändern wobei ich aber denke das es nicht nötig ist.
Wenn ich eine Variable als integer definiere dann ist automatisch wenn nichts anderes angegeben wird der erste wert = 0 oder liege ich da falsch?
In dem Fall muss ich nReading nicht extra 0 zuweisen.

Aber wenn das für dich besser ist? Kein Problem.
Delphi-Quellcode:
  try
    try
      nReading := 0;
      while not eof(ParseFile) do
      begin
        ReadLN(ParseFile, sBuffer);
        AppendToLinkedList(nReading, sBuffer);
        inc(nReading);
      end;
    except
      raise Exception.Create(SysErrorMessage(GetLastError));
    end;
  finally
    CloseFile(ParseFile);
  end;
gruss

DasWolf 18. Feb 2019 09:10

AW: String freigeben nur wo?
 
Zitat:

Zitat von EWeiss (Beitrag 1425840)
Wenn ich eine Variable als integer definiere dann ist automatisch wenn nichts anderes angegeben wird der erste wert = 0 oder liege ich da falsch?

Falsch.

Uwe Raabe 18. Feb 2019 09:11

AW: String freigeben nur wo?
 
Zitat:

Wenn ich eine Variable als integer definiere dann ist automatisch wenn nichts anderes angegeben wird der erste wert = 0 oder liege ich da falsch?
Das gilt nur bei globalen Variablen und Feldern in Klassen. Lokale Variablen enthalten zufällige Inhalte.

Ich würde es aber in jedem Fall vor der while-Schleife setzen, denn dann könnte man die procedure auch mehrmals aufrufen.


In dem Code habe ich sonst nur das Freigeben der Strings entfernt und eine Schleife zum Freigeben der linked List eingebaut. Vielleicht ist das MemoryLeak damit ja schon behoben.
Delphi-Quellcode:
procedure FormatINI(Filename: string);
var
  sBuffer: string;
  ParseFile: TextFile;
  StringList: TStringList;
  StringParse: string;

procedure AppendToLinkedList(nReading: Integer; sBuffer: string);
begin

  New(FPBuffer);

  if nReading = 0 then
  Begin
    New(FToPBuffer);
    LineStart := FToPBuffer;
    LineStart^.Nr := 0;
  end;

  FPBuffer^.Nr := nReading;
  FPBuffer^.Str := sBuffer;
  LineStart^.Max := FPBuffer^.Nr;
  FToPBuffer^.Ptr := FPBuffer;
  FToPBuffer := FPBuffer;
end;

begin
  StringList := TStringList.Create;

  Assignfile(ParseFile, Filename);
  reset(ParseFile);

  try
    try
      nReading := 0;
      while not eof(ParseFile) do
      begin
        ReadLN(ParseFile, sBuffer);
        AppendToLinkedList(nReading, sBuffer);
        inc(nReading);
      end;
    except
      raise Exception.Create(SysErrorMessage(GetLastError));
    end;
  finally
    nReading := 0;
    CloseFile(ParseFile);
  end;

  FPBuffer := LineStart;
  while (FPBuffer.Nr <= LineStart.Max - 1) do
  begin
    FPBuffer := FPBuffer.Ptr;
    StringParse := LeftTrim(FPBuffer.Str);
    if Length(StringParse) <> 0 then
      StringList.Add(FPBuffer^.Str);

    if FPBuffer.Nr > 0 then
      if LeftStr(FPBuffer^.Str, 1) = '[' then
        StringList.Insert(StringList.IndexOf(StringParse), '');
  end;

  StringList.SaveToFile(Filename);
  StringList.Free;

  { linked List freigeben }
  while LineStart <> nil do begin
    FPBuffer := LineStart;
    LineStart := FPBuffer.Ptr;
    { Bei Dispose kümmert sich der Compiler um das Finalize }
    Dispose(FPBuffer);
  end;
  { keine dangling Pointer hinterlassen }
  FPBuffer := nil;
  FToPBuffer := nil;

end;

EWeiss 18. Feb 2019 09:24

AW: String freigeben nur wo?
 
danke dir Uwe..

Ich habe es vorher mal so versucht.

Delphi-Quellcode:
  if not Assigned(FPBuffer) then
    New(FPBuffer);
  //.....
  finalize(FPbuffer^);
  Dispose(FPBuffer);
  FPBuffer := nil;

  finalize(LineStart^);
  Dispose(LineStart);
  LineStart := nil;
Ist das gleiche Ergebnis wie bei dir.
Ich bin darauf gekommen weil du sagtest
Zitat:

Was mir auffällt ist, daß für jede Zeile im AppendToLinkedList ein New() aufgerufen wird, es aber nur ein einziges Dispose() gibt.
Es ist ja eigentlich nicht nötig jedes mal den FPBuffer neu zu erstellen wenn ich es einmal getan habe.
Doch ist nötig ;) Deshalb nehme ich deine Lösung. Danke noch mal.

Beide Varianten melden jetzt keinen Speicherleck mehr.

Frage mich nur warum beim doppelklick in dem EurekaLog Dialog auf diese zeile gesprungen wird.
Delphi-Quellcode:
AppendToLinkedList(nReading, sBuffer); // sBuffer soll einen Memoryleak produzieren.


das irritiert doch etwas denn sBuffer kann ich dort nicht einfach löschen.

Zitat:

Zitat:

Zitat von DasWolf (Beitrag 1425842)
Zitat:

Zitat von EWeiss (Beitrag 1425840)
Wenn ich eine Variable als integer definiere dann ist automatisch wenn nichts anderes angegeben wird der erste wert = 0 oder liege ich da falsch?

Falsch.


Nun pauschal kann man das nicht sagen siehe die Erklärung dazu von Uwe.

gruss

Uwe Raabe 18. Feb 2019 09:54

AW: String freigeben nur wo?
 
Zitat:

Zitat von EWeiss (Beitrag 1425845)
Es ist ja eigentlich nicht nötig jedes mal den FPBuffer neu zu erstellen wenn ich es einmal getan habe.

Dann gibt es doch aber auch nur einen Record in der linked List, oder?

BTW, warum überhaupt eine linked List? Eine TList<PassenderRecordTyp> ist doch viel einfacher zu handhaben. Das Ptr Feld im Record wäre dann auch obsolet.


Zitat:

Zitat von EWeiss (Beitrag 1425845)
Frage mich nur warum beim doppelklick in dem EurekaLog Dialog auf diese zeile gesprungen wird.
Delphi-Quellcode:
AppendToLinkedList(nReading, sBuffer); // sBuffer soll einen Memoryleak produzieren.
das irritiert doch etwas denn sBuffer kann ich dort nicht einfach löschen.

Der geleakte Speicher wird halt genau an der Stelle zugewiesen. Der String wird dann dem Record-Feld zugewiesen, was den Referenzzähler erhöht. Die Freigabe von sBuffer gibt dann aber eben nicht den Stringspeicher frei, da dieser ja noch von dem Record-Feld referenziert wird. Erst wenn der Record mit Dispose freigegeben wird, erfolgt auch die Freigabe des Stringspeichers.

Eurekalog kann aber nur erkennen, wo der Speicher alloziert wurde und das ist eben in dieser Zeile.

EWeiss 18. Feb 2019 10:00

AW: String freigeben nur wo?
 
Sorry Uwe war vorschnell geurteilt ;)
Habe es editiert.

Zitat:

Ist das gleiche Ergebnis wie bei dir.
Ich bin darauf gekommen weil du sagtest
Zitat:

Was mir auffällt ist, daß für jede Zeile im AppendToLinkedList ein New() aufgerufen wird, es aber nur ein einziges Dispose() gibt.
Es ist ja eigentlich nicht nötig jedes mal den FPBuffer neu zu erstellen wenn ich es einmal getan habe.
Doch ist nötig ;) Deshalb nehme ich deine Lösung. Danke noch mal.
Zitat:

BTW, warum überhaupt eine linked List? Eine TList<PassenderRecordTyp> ist doch viel einfacher zu handhaben. Das Ptr Feld im Record wäre dann auch obsolet.
Na ja NonVcl halt wenn nicht unbedingt nötig verwende ich die Win32API.

gruss

Neutral General 18. Feb 2019 10:08

AW: String freigeben nur wo?
 
Zitat:

Zitat von EWeiss (Beitrag 1425849)
Zitat:

BTW, warum überhaupt eine linked List? Eine TList<PassenderRecordTyp> ist doch viel einfacher zu handhaben. Das Ptr Feld im Record wäre dann auch obsolet.
Na ja NonVcl halt wenn nicht unbedingt nötig verwende ich die Win32API.

TList<T> ist NonVCL

EWeiss 18. Feb 2019 10:11

AW: String freigeben nur wo?
 
Zitat:

Zitat von Neutral General (Beitrag 1425851)
Zitat:

Zitat von EWeiss (Beitrag 1425849)
Zitat:

BTW, warum überhaupt eine linked List? Eine TList<PassenderRecordTyp> ist doch viel einfacher zu handhaben. Das Ptr Feld im Record wäre dann auch obsolet.
Na ja NonVcl halt wenn nicht unbedingt nötig verwende ich die Win32API.

TList<T> ist NonVCL

Du hast recht :)
Habe es nun mal so gemacht.
Und mit Uwes Hilfe wird jetzt alles korrekt frei gegeben.

Danke nochmals.

gruss


Alle Zeitangaben in WEZ +1. Es ist jetzt 23:50 Uhr.
Seite 1 von 2  1 2      

Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz