Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Object-Pascal / Delphi-Language (https://www.delphipraxis.net/32-object-pascal-delphi-language/)
-   -   Delphi Funktion verursacht Memory-Leak >>> Warum ? (https://www.delphipraxis.net/72470-funktion-verursacht-memory-leak-warum.html)

jensw_2000 30. Jun 2006 20:22


Funktion verursacht Memory-Leak >>> Warum ?
 
Hi,
folgende Funktion wird in meinem Programm einige 100000 mal pro Tag aufgerufen. Das Programm belegt morgens nach dem Start etwa 17 MB. Abends belegt das Tool etwa 80 MB.

Folgende Funktion scheint dafür verantwortlich zu sein ...

Delphi-Quellcode:
CallJob.Rufnummer := RemoveChars(CallJob.Rufnummer);
Delphi-Quellcode:
function RemoveChars(Rufnummer: string): string;
var i: Integer;
begin
  result := '';

  if length(Rufnummer) = 0 then exit;

  Rufnummer := stringreplace(Rufnummer, '+', '00', []);

  for i := 0 to length(Rufnummer) do
    if Rufnummer[i] in ([#48..#57]) then result := result + Rufnummer[i];

  Setlength(Rufnummer,0);   

end;
Offensichtlich wird irgend ein String nicht aus dem Speicher entfernt.
Wenn ich den Funktionsaufruf "CallJob.Rufnummer := RemoveChars(CallJob.Rufnummer);" auskommentiere, ist das Speicherleck weg ... :roll:

Wo und Wie kann ich den Stringspeicher wieder sauber freigeben ?

Bernhard Geyer 30. Jun 2006 20:26

Re: Funktion verursacht Memory-Leak >>> Warum ?
 
Las mal den Setlength-Aufruf weg und die Schleife sollt auch von 1 beginnen.

negaH 30. Jun 2006 20:48

Re: Funktion verursacht Memory-Leak >>> Warum ?
 
ein "anfälliger" Programmierstil den du hast

Delphi-Quellcode:
function RemoveChars(const Rufnummer: String): String;
var
  I: Integer;
  R,T: String;
begin
  R := '';
  if Rufnummer <> '' then
  begin
    T := StringReplace(Rufnummer, '+', '00', []);
    for I := 1 to Length(T) do
      if T[I] in ['0'..'9'] then
        R := R + T[I];
  end;
  Result := R;
end;
Was auffällt ist die klarere Struktur einer modularen Programmierung. Das heist klare Eingabeparameter die readonly/konstant sind, klare lokale Variablen in denen das Prozessing stattfindet und klare Ausgabeparameter die am Schluß der Funktion erst gefüllt werden, quasi-konstant.
Dann die Benutzung struktutierter Bedingungen, statt einem Exit also ein Begin End Block.
Und die Benutzung lesbarer Char Konstanten in der in [] Abfrage, was im Gewgensatz zu #48 und #57 oder wenigsten #$30 und #$39 weit intuitiver ist. Diese Kleinigkeiten machen alles verständlicher und wir als Programmierer können das bischen Glukose in unserem Hirn auf die logische Fehlervermeidung konzentrieren.

Klar, es gibt nun noch Optimierungen zu Gunsten der Performance, aber das ist für dein Problem erstmal irrelevant.
Zb.

Delphi-Quellcode:
function RemoveChars(const Rufnummer: String): String;
var
  I,J: Integer;
  R: String;
begin
  R := '';
  if Rufnummer <> '' then
  begin
    J := 0;
    SetLength(R, Length(Rufnummer) * 2); // doppelte Länge preallozieren, bei '+' Substitution mit '00' ergo 1 zu 2
    for I := 1 to Length(Rufnummer) do
      if Rufnummer[I] in ['0'..'9'] then
      begin
        Inc(J);
        R[J] := T[I];
      end else
        if Rufnummer[I] = '+' then
        begin
          R[J +1] := '0';
          R[J +2] := '0';
          Inc(J, 2);
        end;
    SetLength(R, J);
  end;
  Result := R;
end;
Diese Funktion prealloziert den LongString in R schon im vorhinein. Statt per Konkatenation -> R := R + x nun viele Reallozierungen dieses String durchzuführen (jedesmal ein Aufruf zum Speichermanager und auch Kopieren dieses Speicherbereich), kopieren wir nur noch das entsprechende Zeichen. Am Schluß setzen wir die Länge von R nur noch auf die Anzahl tatsächlich kopierter Zeichen. Das dürfte in der Peformance weitaus effizienter sein.


Gruß Hagen

jensw_2000 30. Jun 2006 21:42

Re: Funktion verursacht Memory-Leak >>> Warum ?
 
Ich habe alle 3 Varianten getestet (Korrektur meiner Funktion und die beiden von Hagen).
Das Problem scheint weiterhin zu bestehen.

Mit dem Aufruf von RemoveChars nimmt der belegte Speicher stätig zu.
(nach 5.000 Durchläufen rund 0,5 MB, nach 30.000 Durchläufen knapp 1 MB)
Ohne den Funktionsaufruf bleibt der belegte Arbeitsspeicher nahezu konstant bei 17,5 MB.

Ist es relevant, asynchron aus mehreren Threads aufgerufen wird ?

omata 30. Jun 2006 22:38

Re: Funktion verursacht Memory-Leak >>> Warum ?
 
_

negaH 30. Jun 2006 22:45

Re: Funktion verursacht Memory-Leak >>> Warum ?
 
Also an deiner RemoveChars Funktion kann es nicht liegen (wenn du die Zählervariable I auf 1 gesetzt hast).

Meine Vermutung ist nun folgende:
Du benutzt diese Funktion in einem Thread. Diese Funktion ist in diesem Thread die Einzigste die mit LongStrings arbeitet, also Speicher beim Speichermanager anfordert. Und nun, ich vermute du nutzt einen anderen Speichermanager ! sowas wie FastMM (wobei der im Grunde sauber ist). Es gibt aber auch andere MMs die eben gerade bei Threads versprechen schneller zu sein als der Borland eigene und das nur erreichen weil sie für jeden Thread separate MMs installieren die aber bei Beendigung der Threads nicht terminiert werden. Also, könnte es sein das du einen anderen MM installiert hast ?

Ansonsten wüsste ich keine Erklärung ohne deinen Source des Threads gesehen zu haben.

Gruß Hagen

jensw_2000 1. Jul 2006 01:59

Re: Funktion verursacht Memory-Leak >>> Warum ?
 
@Hagen
Ich habe mit vor einigen Tagen zur Fehlersuche FastMM 4 installiert und "RemoveChars" ist in der Tat das Einzige Aufruf einer Thread-externen Funktion, bei dem LongStrings als Parameter verwendet werden.
Die Threads werden beim Dienststart erzeugt und beim Dienst-Stop beendet und freigegeben. Das Grundgerüst habe ich auf Alzaimar's genialem Threads & Jobs Beispiel aufgebaut.
Die Jobs sind einfache Objekte, welche u.A. 2 Longstrings enthalten. Die Job-Objekte befinden sich in einer TObjectlist und werden mittels TObjectlist.Extract von einem freien WorkerThread abgeholt. Der WorkerThread liest als erstes die Werte aus dem Job-Objekt in (Thread-)lokale Variablen ein und gibt das Job-Objekt sofort frei.
Dannach werden die Daten durch ein an den Thread gebundenes COM Objekt bearbeitet und letztlich in einer DB gespeichert. Der Workerthread wieder "inaktiv" extrahiert dann das nächste freie Job-Object aus der Jobliste.

Ich baue am Sonntag den Borland MM wieder ein und mache ein paar einen Testläufe.
Hoffentlich erledigt sich das Leak-Problem damit.

Erstmal vielen Dank für deine Hilfe.


Schöne Grüße,
Jens
:hi:


Alle Zeitangaben in WEZ +1. Es ist jetzt 01:35 Uhr.

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