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/)
-   -   Memory-Leaks in TEncoding (https://www.delphipraxis.net/196816-memory-leaks-tencoding.html)

freimatz 22. Jun 2018 07:09

Delphi-Version: 5

Memory-Leaks in TEncoding
 
Hallo zusammen,
ich bin auf der Suche nach Memory-Leaks. Dazu verwende ich madexcept, spielt aber für meine Frage wohl keine Rolle. Ich meine in TEncoding etwas gefunden zu haben, bin mir aber nicht sicher. Also ...
Bei debuggen (in SysUtils Tokio) komme ich da durch:
Delphi-Quellcode:
class function TEncoding.GetBufferEncoding(const Buffer: TBytes; var AEncoding: TEncoding;
  ADefaultEncoding: TEncoding): Integer;
...
var
  Preamble: TBytes;
begin
  Result := 0;
  if AEncoding = nil then
  begin
    // Find the appropraite encoding
    if ContainsPreamble(Buffer, TEncoding.UTF8.GetPreamble) then
      AEncoding := TEncoding.UTF8
    else if ContainsPreamble(Buffer, TEncoding.Unicode.GetPreamble) then
...
end;
Dort bei "TEncoding.Unicode.GetPreamble" rein komme ich zu
Delphi-Quellcode:
class function TEncoding.GetUnicode: TEncoding;
var
  LEncoding: TEncoding;
begin
  if FUnicodeEncoding = nil then
  begin
    LEncoding := TUnicodeEncoding.Create;
    if AtomicCmpExchange(Pointer(FUnicodeEncoding), Pointer(LEncoding), nil) <> nil then
      LEncoding.Free;
{$IFDEF AUTOREFCOUNT}
    FUnicodeEncoding.__ObjAddRef;
{$ENDIF AUTOREFCOUNT}
  end;
  Result := FUnicodeEncoding;
end;
Bei "TUnicodeEncoding.Create" wird Speicher erzeugt. Bei LEncoding.Free; komme ich nicht vorbei. Das wird dann bei Result übergeben und weiter oben nirgendwo freigegeben. Ich sehe auch nicht dass es eine Klasse mit Referenzzählung wäre.
Was sehe ich falsch? Oder ist das wirklich ein Leak?

Der schöne Günther 22. Jun 2018 07:26

AW: Memory-Leaks in TEncoding
 
Das ist in Ordnung: GetUnicode() erstellt lokal erst einmal in TEncoding und legt es in LEncoding ab. Dann schaut er ob TEncoding.Unicode (class var FUnicodeEncoding) schon belegt ist. Wenn ja dann sagt er "Ok, dann halt nicht" und gibt sein LEncoding direkt wieder frei. Wenn nicht, dann schreibt er in class var FUnicodeEncoding die Referenz auf LEncoding.

So viel zum Thema Delphi sei immer super lesbar 8-)

Der Klassendestruktor von TEncoding ruft FreeEncodings() auf, das gibt am Programmende auch das TEncoding.Unicode wieder frei.

Schokohase 22. Jun 2018 08:06

AW: Memory-Leaks in TEncoding
 
Das was man dort sieht ist die übliche Vorgehensweise für die thread-safe und lock-free Erstellung einer (hier globalen) Instanz.

Mit
Delphi-Quellcode:
TInterlocked
könnte man das noch etwas übersichtlicher gestalten:
Delphi-Quellcode:
class function TEncoding.GetUnicode: TEncoding;
var
  LEncoding: TEncoding;
begin
  if FUnicodeEncoding = nil then
  begin
    LEncoding := TUnicodeEncoding.Create;
    try
      LEncoding := TInterlocked.CompareExchange<TEncoding>(FUnicodeEncoding, LEncoding, nil);
    finally
      LEncoding.Free;
    end;
  end;
  Result := FUnicodeEncoding;
end;

freimatz 22. Jun 2018 12:48

AW: Memory-Leaks in TEncoding
 
Zitat:

Zitat von Der schöne Günther (Beitrag 1405508)
Der Klassendestruktor von TEncoding ruft FreeEncodings() auf, das gibt am Programmende auch das TEncoding.Unicode wieder frei.

Da hast Du recht. Danke. Leider folgt die Freigabe erst nachdem madExcept die Leaks analysiert. Muss da mal weiterschauen was ich da machen kann.

Uwe Raabe 22. Jun 2018 12:52

AW: Memory-Leaks in TEncoding
 
Zitat:

Zitat von freimatz (Beitrag 1405538)
Muss da mal weiterschauen was ich da machen kann.

Das "No-Leak" mit
Delphi-Quellcode:
ThisIsNoLeak
anmelden.

Der schöne Günther 22. Jun 2018 15:47

AW: Memory-Leaks in TEncoding
 
Oder selbst vorzeitig
Delphi-Quellcode:
TEncoding.FreeEncodings()
aufrufen.

Stevie 22. Jun 2018 16:17

AW: Memory-Leaks in TEncoding
 
Zitat:

Zitat von Schokohase (Beitrag 1405512)
Das was man dort sieht ist die übliche Vorgehensweise für die thread-safe und lock-free Erstellung einer (hier globalen) Instanz.

Mit
Delphi-Quellcode:
TInterlocked
könnte man das noch etwas übersichtlicher gestalten

Jo, was leider a) nicht vernünftig geinlined wird und b) unter ARC zu unnötigem rumgerefcountere führt.

Schokohase 22. Jun 2018 16:39

AW: Memory-Leaks in TEncoding
 
Zitat:

Zitat von Stevie (Beitrag 1405557)
Jo, was leider a) nicht vernünftig geinlined wird und b) unter ARC zu unnötigem rumgerefcountere führt.

Ich sagte ja auch übersichtlicher (= besser lesbar), was aber nicht immer zwangsläufig technisch gesehen besser sein muss.

Wenn ich mir den Code so ansehe, dann denke ich mir, ob der nicht eher so lauten müsste:
Delphi-Quellcode:
class function TEncoding.GetUnicode: TEncoding;
var
  LEncoding: TEncoding;
begin
  if FUnicodeEncoding = nil then
  begin
    LEncoding := TUnicodeEncoding.Create;
    if AtomicCmpExchange(Pointer(FUnicodeEncoding), Pointer(LEncoding), nil) <> nil then
      LEncoding.Free
    else begin
{$IFDEF AUTOREFCOUNT}
      FUnicodeEncoding.__ObjAddRef;
{$ENDIF AUTOREFCOUNT}
    end;
  end;
  Result := FUnicodeEncoding;
end;
Es wird zwar relativ selten vorkommen, dass mehrere Threads gleichzeitig so eine Encoding Instanz haben möchten und diese noch nicht initialisiert ist, aber wenn man schon damit anfängt, dann sollte man es doch auch richtig machen.

Nehmen wir als 4 Threads, die alle auf die Situation treffen
Delphi-Quellcode:
FUnicodeEncoding = nil
, alle 4 erzeugen eine Instanz und versuchen diese per
Delphi-Quellcode:
AtomicCmpExchange
an dem Mann zu bringen, wobei hier gesichert nur einer gewinnt.

Allerdings wird nun trotzdem 4 mal
Delphi-Quellcode:
FUnicodeEncoding.__ObjAddRef;
ausgeführt und das würde zu einem MemLeak führen, denn am Ende werden 3 Referenzen übrig bleiben.

Ja nichts was einen jetzt so richtig nervös machen kann, aber Leak ist Leak (oder ich habe einfach nur einen Knick in der Optik).

PS: Das wäre mit
Delphi-Quellcode:
TInterlocked.CompareExchange
nicht passiert

himitsu 22. Jun 2018 17:02

AW: Memory-Leaks in TEncoding
 
hmmmm...,

wenn die Referenz übertragen wurde, dann gibt es nun eine Referenz mehr ... konnte nicht automatisch gezählt werden, weil
AtomicCmpExchange durch die "Pointer" die Referenzzählung umgeht.

Wurde es nicht übertragen, weil in der Zwischenzeit ein anderer Thread schneller war, dann bleiben die Referenzen, aber das nun unnötige neue TEncoding muß weg.

Also entweder-oder, aber nicht immer, das AddRef.



Könntest Recht haben. :gruebel:

Stevie 22. Jun 2018 17:04

AW: Memory-Leaks in TEncoding
 
https://quality.embarcadero.com/browse/RSP-19796 8-)


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