Delphi-PRAXiS
Seite 1 von 2  1 2      

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Object-Pascal / Delphi-Language (https://www.delphipraxis.net/32-object-pascal-delphi-language/)
-   -   Delphi Extremer Speicherfraß durch Strings/StringLists (?) (https://www.delphipraxis.net/10073-extremer-speicherfrass-durch-strings-stringlists.html)

nTE 11. Okt 2003 01:06


Extremer Speicherfraß durch Strings/StringLists (?)
 
Also, mein Problem ist folgendes:

Ich habe ein Programm welches Informationen einliest aus TEmbeddedWB. (ach nee ;)
Mit SaveToStrings speichere ich den Source der Seite in einer TStringList die jedes mal Lokal erstellt und wieder gefreet wird. (Ich bin mir ziemlich sicher, dass ich das try..finally Konstrukt richtig anwende und bei jedem UP-Aufruf wird die Codezeile mit .Free auch abgearbeitet)

Ich verwende sakura's Funktion NextPos um relevante Daten im Source zu finden und lege die in einem Array[1..5] of AnsiString (global) ab. Nach einer bestimmten Anzahl von Aufrufen, speichere ich den Inhalt des Strings in einer Textdatei per Append und mache dann z.B. sMyString[1] := ''; um den String wieder freizugeben.
Mache ich damit schonmal was falsch?
(Und bitte, fragt mich nicht warum ich so'n Scheiss mit dem Array mache, nehmt einfach an es ginge nicht anders. ;)

Tjo, sonst gibt es da noch 2 StringLists, aber die enthalten nicht wirklich viel (max 50KB zusammen) und werden bei Start des Programmes created und bei Beenden gefreet.

Viel mehr Variablen (ausser vielleicht ein bis zwei Schleifenvariablen) habe ich nicht und kann mir nicht erklären, wie das Programm so derbe viel Speicher fressen kann. (Da muss irgendwo ein schwarzes Loch drin sein)

Ich habe es mal eine Stunde laufen lassen (ja, es gab schon einiges zu tun ;) und gegen Ende nutze es 540MB und das Tolle ist, die relevanten Daten, gespeichert in der Textdatei, brauchten am Ende nur ~700KB.


Das ist doch ein erstaunlich großer Unterschied und daher dachte ich mir, frage ich mal hier, vielleicht weiss jemand Rat. Ich kann mir nämlich nur noch an den Kopf fassen.

Christian Seehase 11. Okt 2003 02:16

Re: Extremer Speicherfraß durch Strings/StringLists (?)
 
Moin nTE,

ein bisschen Source wär' nicht schlecht. ;-)

nTE 11. Okt 2003 08:56

Re: Extremer Speicherfraß durch Strings/StringLists (?)
 
Oh nein, das habe ich befürchet.
Ähm.. also der Code ist wirklich ausgesprochen hässlich und ich hoffe mir ist niemand böse, wenn ich ihn auf das Wesentliche beschränke in der Darstellung hier. :P

Delphi-Quellcode:
procedure Scan(Sender: TObject; sSearchStr);
var
  i, j: Integer;
  sSource: TStringList;
  iPos: Cardinal;
  TempStr: AnsiString;
  DF: Textfile;

begin
  if Sender is TEmbeddedWB then
    begin
      sSource := TStringList.Create;
      try
        with Sender as TEmbeddedWB do
          SaveToStrings(sSource);

        iPos := 0;

        // jetzt kämen zwei for-schleifen :P
        //for i := ...
        //for j := ...
            begin
              iPos := NextPos(sSearchStr, sSourceText, iPos + 1);
              TempStr := Copy(sSource.Text, iPos, 250);

              // kopiert die relevante Information (bis zum nächsten '<')
              // übrigens, da Length Integer und NextPos Cardinal zurückliefert, bekomme ich..
              // ..bei jedem Aufruf dieser Art "Vorzeichenbehaftete und -lose Typen werden kombiniert.."
              // aber dagegen kann ich wohl nichts machen oder? ^^;
              TempStr := Copy(TempStr, 1, Length(TempStr) - (Length(TempStr) - NextPos('<', TempStr, 1) + 1));

              Lines[i] := Lines[i] + TempStr;
              // es folgen weitere 2-3 (je nach if Resultat) solcher Zeilen :P
              // ...
            end;
          // das Speichern des Inhaltes, ab und zu mal (if Verzweigung)
          begin
            for i := 2 to 5 do
              begin
                Lines[1] := Lines[1] + Lines[i];
                Lines[i] := '';
              end;

            AssignFile(DF, SAVE_FILE);
            Append(DF);
            Write(DF, Lines[1]);
            Flush(DF);
            CloseFile(DF);
            Lines[1] := '';
          end;
        with Sender as TEmbeddedWB do
          // URL wird abhängig von den gefundenen Ergebnissen verändert um sich durchzunavigieren :P
          Navigate(URL);
      finally
        sSource.Free;
      end;
    end;
end;
Diese Prozedur wird von TEmbeddedWB.OnDownloadComplete aufgerufen und nachdem ich es mal ein paar Stunden laufen ließ, wurde sie bestimmt an die 40000 Mal aufgerufen.

Mmh.. also ich könnte verstehen wenn euch meine Art meinen Code zu präsentieren gelinde gesagt zu blöd ist, aber ich würde mich trotzdem immer über Hilfe freuen. ;)

[edit=Daniel B]Delphi-Tags korrigiert. Mfg, Daniel B[/edit]

Ghostwalker 11. Okt 2003 09:32

Re: Extremer Speicherfraß durch Strings/StringLists (?)
 
Also.....ich kenn zwar das Problem nicht. Aber 3 Dinge sind mir aufgefallen.

1. Diese Codezeile

Code:
TempStr := Copy(TempStr, 1, Length(TempStr) - (Length(TempStr) - NextPos('<', TempStr, 1) + 1));
der String wird die befüllt (length(tempstr)-length(tempstr)) = 0 - nochwas ergibt negativen wert...!!!


2. sSource wird immer wieder neu erzeugt....-> ineffektiv

Lieber am Programmstart create, am ende free und hier immer nur ssource.clear;


3.Datei

Assign, Append und Close würd ich ebenfalls außerhalb machen. Das frist auch ewig zeit.


Obwohl ich auch einiges mit TStrings/TStringlist mach, hab ich bisher nix feststellen können, das da irgendwelche Speicherleaks auftreten.

Propier mal ob du vielleicht mit dem Programm "Memproof" was rausfindest.

http://www.automatedga.com (memproof)

nTE 11. Okt 2003 11:03

Re: Extremer Speicherfraß durch Strings/StringLists (?)
 
Liste der Anhänge anzeigen (Anzahl: 1)
Erstmal danke für die Antwort, ich habe mir alles mal angeschaut was du gesagt hast (vor allem MemProof) und habe folgendes zu berichten:

Zu 1.
Ich überprüfe vorher (sorry, hab ich nicht aufgeführt) ob iPos > 0 ist (also ob überhaupt was gefunden wurde) und der hintere Teil ist nicht "(length(tempstr)-length(tempstr))" sondern "Length(TempStr) - (Length(TempStr) - NextPos('<', TempStr, 1) + 1)" und daher wird das nie kleiner Null, ausser in ganz TempStr befindet sich kein '<', was ich aber Aufgrund der Kentnisse über die zu erfassende Struktur ausschließen kann. (Es ist sicher immer möglich und schlecht programmiert sowieso, soviel ist sicher =)

Zu 2.
Guter Tipp, habe ich gleich mal geändert.


Zu 3.
Ok, wenn kein Memory mehr leakt, werde ich das alles in einem Schwung in die Datei schreiben (sind ja wie gesagt nur ~700KB). =)


So, erstmal vorneweg, das Programm verwendet schon 6.5 MB nach dem Start.

Ich würde das Speicherloch eindeutig dem Internet Explorer zuschieben.
Wie in der angehangenen Datei zu erkennen, sind die meisten (äh, fast alle?) Fehler beim Speicherfreigeben unter Beteiligung von mshtml.dll und shdocvw.dll.

Ich benutze IE 5.0 unter Win2k mit SP3, d.h. IE 5.00.3502.1000 (128-Bit).
Ich habe schon ein wenig über Leaks im IE 5 gefunden auf der Microsoft-Page aber die sollen angeblich mit IE 5.0 SP3 behoben worden sein.
Müsste ich jetzt auf IE 6 umsteigen (was ich nur seeehr ungern würde) und kann es dabei mit meinem Programm Probleme geben? =)

Christian Seehase 11. Okt 2003 13:12

Re: Extremer Speicherfraß durch Strings/StringLists (?)
 
Moin nTE,

so unmittelbar ist nichts zu sehen :?
Da ich jetzt TEmbeddedWB nicht habe:
Wo kann ich mal eine Doku zu SaveToStrings nachlesen?
Im Moment habe ich diese Methode im Verdacht.

nTE 11. Okt 2003 18:41

Re: Extremer Speicherfraß durch Strings/StringLists (?)
 
Auf der Page steht nur, dass SaveToStrings den Source nach TStrings speichert. :P

Embedded WB Homepage

Daher habe ich mal das Nötige rauskopiert (hoffe ich).

Delphi-Quellcode:
function TEmbeddedWB.SaveToStrings(AStrings: TStrings): HRESULT;
begin
//  while ReadyState <> READYSTATE_COMPLETE do
//    Forms.Application.ProcessMessages;
  if Assigned(document) then
    Result := SaveDocToStrings(Document, AStrings)
  else Result := S_FALSE;
end;

function SaveDocToStrings(Doc: IDispatch; var AStrings: TStrings): HResult;
var
  IpStream: IPersistStreamInit;
  AStream: TMemoryStream;
begin
  AStream := TMemoryStream.Create;
  try
    IpStream := doc as IPersistStreamInit;
    if not Assigned(IpStream) then Result := S_FALSE else
      if Succeeded(IpStream.save(TStreamadapter.Create(AStream), TRUE))
        then begin
        AStream.Seek(0, 0);
        AStrings.LoadFromStream(AStream);
        Result := S_OK;
      end else Result := S_FALSE;
  except
    Result := S_FALSE;
  end;
  AStream.Free;
end;
In der oberen Funktion habe ich die beiden Codezeilen auskommentiert, da sonst SaveToStrings nie funktioniert, wenn z.B. ein JavaScript ständig den Inhalt ändert (z.B. bei einem Counter oder einer Uhr).
Ausserdem funktioniert SaveFrameToStrings auch ohne das. ;)

Christian Seehase 11. Okt 2003 18:53

Re: Extremer Speicherfraß durch Strings/StringLists (?)
 
Moin nTE,

mal abgesehen davon, dass AStream dort nicht mit einem try/finally Block geschützt ist, bei einer Exception der MemoryStream nicht wieder freigegeben wird, kann ich auch da nichts finden.

nTE 11. Okt 2003 19:00

Re: Extremer Speicherfraß durch Strings/StringLists (?)
 
Da ich immer (hab's überprüft ;) einen String zurückbekomme kann man wohl ausschließen, dass eine Exception aufgetreten ist, oder?

Wenn dem so ist, würde ich es echt auf die shdocvw.dll/mshtml.dll schieben. ;)

nTE 11. Okt 2003 22:35

Re: Extremer Speicherfraß durch Strings/StringLists (?)
 
Okay, ich habe es einfach mal mit sSource.LoadFromFile als Ersatz probiert (den Rest so gelassen), um festzustellen ob es an SaveToStrings liegt und du hattest Recht Christian, da ist der Wurm drin.

Ich habe mal geschaut was bei TEmbeddedWB so falsch sein könnte und bin bisher nur auf das gestoßen:

Delphi-Quellcode:
constructor TEmbeddedWb.Create(Owner: TComponent);
var
  Buf: array[1..10] of Char;
begin
  FfpExceptions := True;
  inherited;
{$IFDEF VER120}
  enablemessagehandler;
{$ENDIF}
  GetDDEVariables;
  // Compiler: Symbol 'AllocateHWnd' wird abgelehnt
  DDEHWnd := AllocateHWnd(DDEWndProc);
  .
  .
  .
end;

destructor TEmbeddedWb.Destroy;
begin
  // Compiler: Symbol 'DeAllocateHWnd' wird abgelehnt
  DeAllocateHWnd(DDEHwnd);
  .
  .
  .
end;

// Folgendes aus function SaveDocToStrings(Doc: IDispatch; var AStrings: TStrings): HResult;
// mehr Quelltext, siehe oben ;)
IpStream := doc as IPersistStreamInit;
// wird nicht wieder freigegeben?  naja, ich hab mal ._Release damit gemacht und es hat nichts gebracht

if Succeeded(IpStream.save(TStreamadapter.Create(AStream), TRUE))
// TStreamadapter hat noch die Eigenschaft Ownership die hier nicht verwendet wird:
// Mit Ownership wird die Eigenschaft StreamOwnership initialisiert, die das verantwortliche TStreamAdapter-Objekt für die Freigabe des in Stream angegebenen Streams im eigenen Destruktor bezeichnet.
//

Mehr fällt mir langsam echt nicht mehr ein. Ich suche momentan noch ein wenig nach Lösungen, aber ich glaube bald lege ich das Programm einfach unter RAM-Hog ab und fass es nicht mehr an. ;)

Ich habe leider keine andere OpenSource TWebBrowser Komponente gefunden, vielleicht hat da ja jemand was für mich? :P


Ach ja, vielleicht könnte ein Mod ja das Thema in "Memory Leak in TEmbeddedWB (SaveTo...)" umbenennen und nach "Internet / IP / LAN" verschieben.


Alle Zeitangaben in WEZ +1. Es ist jetzt 11:04 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