Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Netzwerke (https://www.delphipraxis.net/14-netzwerke/)
-   -   Delphi Torrent Client - socket.sendstream mit Memory Leak (https://www.delphipraxis.net/122833-torrent-client-socket-sendstream-mit-memory-leak.html)

umpani 22. Okt 2008 22:21


Torrent Client - socket.sendstream mit Memory Leak
 
Hallo zusammen,

ich habe versuche jetzt schon mehrere Tage lang ein Memory Leak herauszufinden. Aber nichts klappt bis jetzt.
Ich schreibe ein Programm, das mit anderen Torrent-Clients kommunizieren soll. Dafür werden die einzelnen TCP-Packete in Stream bereitsgestellt und über die Socket-komponente verschickt. Klappt auch alles. Allerdings verbraucht das Programm immer mehr Speicher. Mittels FastMM4 habe ich jetzt einige Problemstellen herausgefunden. Aber eine Lösung habe ich noch nicht gefunden. Wenn ich übrigens das Programm minimiere und wieder maximiere ist der unnötige Speicherbereich wieder freigegeben (natürlich nur um dann direkt wieder zu wachsen),

Hier ist z.B. ein Auszug aus der FastMM Log Datei. An der Stelle, die FastMM bezeichet, erstellt dasProgramm einen Torrent-Handshake und versendet diese zu einem TCPServer.
Code:
A memory block has been leaked. The size is: 8356

This block was allocated by thread 0xD18, and the stack trace (return addresses) at the time was:
402EE2 [system.pas][System][@GetMem][2648]
4241CF [classes.pas][Classes][TMemoryStream.SetCapacity][5591]
424335 [classes.pas][Classes][TMemoryStream.Write][5639]
4C698A [sendestreams.pas][sendestreams][get_handshake][45]
4C7C2C [peerconnect.pas][peerconnect][TPeerconnect.torrent_verbindung_aufbauen][467]
4C7B2D [peerconnect.pas][peerconnect][TPeerconnect.Execute][410]
42A985 [classes.pas][Classes][ThreadProc][9866]
404A7E [system.pas][System][ThreadWrapper][12127]
7C80B683 [Unknown function at GetModuleFileNameA]

The block is currently used for an object of class: Unknown

The allocation number is: 87562

Current memory dump of 256 bytes starting at pointer address 7FBE41F0:
13 42 69 74 54 6F 72 72 65 6E 74 20 70 72 6F 74 6F 63 6F 6C 00 00 00 00 00 00 00 00 87 69 7F 14
D6 F5 33 9C BC FE 57 70 E3 50 0D 5D 0B 7D 8C FB 41 5A 32 35 30 30 42 54 30 30 30 30 30 30 30 30
30 30 30 30 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80
80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80
80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80
80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80
80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80
80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80
. B i t T o r r e n t    p r o t o c o l . . . . . . . . ‡  i   .
Ö  õ  3  µ  Ÿ  þ  W p ã  P . ] . }  ©  û  A Z 2  5  0  0  B T 0  0  0  0  0  0  0  0
0  0  0  0  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €
€  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €
€  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €
€  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €
€  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €
€  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €
Der dazugehörige Delphi-Code:

Delphi-Quellcode:
  try
     torrent.peers.peerliste[peernr].client.Socket.SendStream(get_handshake(torrent));
  finally
  ...
  end;
Delphi-Quellcode:
function get_handshake(input:ttorrent):tstream;
  var
      pstrlen:byte;
      pstr:string;
      reserved:integer;
      infohashhex:string[40];
      infohash:array[1..20] of byte;
      peer_id:string[20];
      i,int:integer;
      s:string;

begin
  result := tmemorystream.create;
  infohashhex := input.infohashhex;
  infohashhex := uppercase(infohashhex);
  for i := 1 to 20 do
    begin
      s := '';
      s := s + infohashhex[i*2-1];
      s := s + infohashhex[i*2];
      infohash [i] := hextoint(s);
    end;

    pstrlen := 19;
    result.Write(pstrlen,1);
    pstr := 'BitTorrent protocol';

    schreibe_string_in_stream(result,pstr);

    reserved := 0;
    result.Write(reserved,sizeof(reserved));
    result.Write(reserved,sizeof(reserved));

    result.Write(infohash,20);

    peer_id := 'AZ2500BT000000000000';
    schreibe_string_in_stream(result,peer_id);

    result.Seek(0,soFromBeginning);
end;
In den Delphihilfen steht, das "socket.sendstream" den übegebenen Stream selbst freigibt. Eine eigenhändige Freigabe verursacht eine exception.

Wie gesagt, von der Funktion her funktionieren der Code einwandfrei.

Ich hoffe ihr könnt mir weiterhelfen.

Gruß Umpani

InfixIterator 23. Okt 2008 16:44

Re: Torrent Client - socket.sendstream mit Memory Leak
 
Vielleicht hilft es, wenn du den Memory Stream wieder frei gibst:
Delphi-Quellcode:
  result := tmemorystream.create;
Da könnte sich einiges summieren, wenn die Funktion öfter aufgerufen wird.

Du musst auch aufpassen, falls der Handshake nur einmal ausgeführt werden soll, dass die Funktion auch nur einmal aufgerufen wird.
Vielleicht liegt auch ein Fehler in der Prozedur schreibe_string_in_stream immerhin wird die auch zweimal aufgerufen...

Roachford 23. Okt 2008 21:35

Re: Torrent Client - socket.sendstream mit Memory Leak
 
Zitat:

Zitat von InfixIterator
Vielleicht hilft es, wenn du den Memory Stream wieder frei gibst:

Lies bitte den Beitrag zuvor nochmal bis zum Ende. Und die Hilfe zu TCustomWinSocket.SendStream() hätte auch geholfen.

@umpani: Der Code ist so in Ordnung. Und soweit wäre vllt. wirklich mal die schreibe_string_in_stream() Methode schön zu betrachten.

Und da der Speicher beim minimieren verkleinert wird, zeigt, dass es wohl nicht unbedingt um ein Speicherleck handelt sondern eher um schlechten Code, der den Speichermanager zur Verzweiflung bringt. FastMM4 ist schon wirklich gut sowas auszugleichen, aber anscheinend schafft er es nicht ganz.

Beispiele dafür ist z.B. schon dein Code zum HexDump: Du baust den String einzeln in der Schleife zusammen und erweiterst ihn jedesmal. Für den Speichermanager und für die Effektivität wäre es deutlich besser vor der Schleife mit SetLength() den String zu setzen und dann die Hexdumps direkt in den String zu zuweisen.

umpani 23. Okt 2008 23:06

Re: Torrent Client - socket.sendstream mit Memory Leak
 
Gibt es denn eine Möglichkeit, den Speicher wieder freizugeben, ohne dass ich das Programm minimiere?

InfixIterator 24. Okt 2008 18:38

Re: Torrent Client - socket.sendstream mit Memory Leak
 
@Roachford socket.sendstream ungleich tmemorystream
Das eine sind Streams von Sockets das andere Streams im Arbeitsspeicher.
(Sockets kommen in dieser Funktion eh nicht direkt vor...)
Der übergebene Stream im Socket mag zwar freigegeben werden, doch wie sieht es in der Funktion aus? :P
(Ich denke die Daten bleiben deswegen im Arbeitsspeicher, da sie nicht sauber gelöscht werden. Erst nach einer Minimierung fällt dem Rechner auf, das die Funktion schon längst abgehackt ist und gibt den Speicher frei.)


@umpani ich habe echt noch nie jemanden gesehen, der ein result so misbraucht wie du :D
Ich hätte anstatt des results eine Variable genommen und erst beim zurückgeben des Funktionswertes die Variable ans Result gehängt.
(So könnte man die Variable auch noch Freigeben...)

Keine Ahnung ob es hilft, aber ein Versuch wäre es Wert und der Code den man ändern müsste ist ja nicht alzu lang.

Edit:
evtl. könnte eine globale Variable mit tmemorystream das Problem abschaffen, da nur eine erstellt wird und benutzt wird.
So würde nicht immer wieder neuer Speicher im Arbeitsspeicher reserviert werden.
Jedoch könnte es aber zu massiven Probleme kommen, wenn du Threads benutzt...

Roachford 24. Okt 2008 20:49

Re: Torrent Client - socket.sendstream mit Memory Leak
 
Zitat:

Zitat von InfixIterator
socket.sendstream ungleich tmemorystream

Jo, das eine ist eine Methode, das andere eine Klasse.

Aber mir ist eben bewusst geworden, dass es sich nicht um TCustomWinSocket Nachfahren handelt. Ich hatte mich auf TServerSocket und TClientSocket bezogen. Von daher sind meine Aussagen hinfällig, da nicht passend.


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