Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Programmieren allgemein (https://www.delphipraxis.net/40-programmieren-allgemein/)
-   -   Delphi EOutOfResources erzeugt Speicherleck (https://www.delphipraxis.net/156523-eoutofresources-erzeugt-speicherleck.html)

Schorschi5566 5. Dez 2010 14:03


EOutOfResources erzeugt Speicherleck
 
Liste der Anhänge anzeigen (Anzahl: 1)
Hallo DP,

ich programmiere gerade ein Multithreading-Projekt, das sehr große JPGs herunterskaliert und erneut speichert.

Dazu werden bis zu 30 Threads verwendet, die mittlerweile keinerlei Speicher- oder Handle-Leaks mehr verursachen.

Trotzdem kommt es natürlich öfter mal zu EOutOfResources, was auch nicht weiter tragisch ist, da sich dann einige Threads einfach selbst killen und sobald es die Speichersituation wieder zulässt, erneut starten. Das funktioniert bestens soweit.

Einziges Manko ist eigentlich, dass die Exception EOutOfResources selbst ein Memoryleak verursacht, welches ich gerne beheben würde. (siehe Screenshot)

Hat jemand einen Tipp, was man da im Fehlerfall freigeben muss oder ist das ein Bug in Delphi?


Grüße,
Uwe

Luckie 5. Dez 2010 14:10

AW: EOutOfResources erzeugt Speicherleck
 
ich programmiere nun schon einige Jahre, aber diese Exception hatte ich noch nie. Ich bin mir ziemlich sicher, dass du irgendwo Ressouren nicht wieder frei gibst.

Schorschi5566 5. Dez 2010 14:24

AW: EOutOfResources erzeugt Speicherleck
 
Wenn 30 Threads versuchen jeweils eine Grafik mit 5000x5000 Punkten in den Speicher zu laden, bekommt man die Meldung recht schnell. ;-)

Ich programmiere auch schon einige Jahre und glaube mir, die Routine verliert keine Resourcen.

Am Skalierungsprozess ist allerdings die Library Graphics32 beteiligt, die auch die Exception wirft. Eventuell allokiert ja dort etwas Sachen, die Delphi dann am Schluß anmeckert.

Aber warum schreibt Delphi dann was von x Mal EOutOfResources?

Luckie 5. Dez 2010 14:29

AW: EOutOfResources erzeugt Speicherleck
 
Wie wäre es die Anzahl der Threads zu reduzieren?

Schorschi5566 5. Dez 2010 14:39

AW: EOutOfResources erzeugt Speicherleck
 
Das passiert schon. Sobald eine EOutOfResources auftritt, beendet sich der entsprechende Thread und macht Luft für die anderen.

Allerdings schwankt das Datenaufkommen und deshalb sollen die Threads wieder erhöht werden, wenn es möglich ist. Nach einer gewissen Zeit werden die Threads also wieder nach und nach gestartet. Bis es wieder knallt. :D

Funktioniert, wie gesagt ausgezeichnet, mal von dem kleinen Speicherleck abgesehen.

Am Schluss eines Laufs, gibt das Programm eine Empfehlung, wie viele Threads man künftig verwenden sollte aber das behebt das Problem ja nun nicht wirklich.

Du meinst also, dass die Exception an sich keinen Speicher verschludert. Kann natürlich sein, dass TBitmap32.Draw die Exception zu spät wirft und nicht alles freigibt, was bis dahin passiert ist. Werde ich mal erforschen.

Danke schon mal. :)

himitsu 5. Dez 2010 14:42

AW: EOutOfResources erzeugt Speicherleck
 
Wenn du schnell was umrechnen willst, dann bringt es sowieso nix, wenn du meh Threads rechnen läßt, als als Recheneinheiten (CPUs/Kerne) vorhanden sind.

PS: 5000x5000 umzurechnen belegt pro Bild schnell mal über 100 MB, da bestimmt über ein Bitmap gerechnet wird, was bei 30 Threads/Bildern wohl etwas viel werden kann.

PSS: Eine Leckprüfung im delphieigenem Speichermanager findet nur Lecks in diesem Teil ... es gibt aber auch noch andere Speichermager und unzählige Handles, welche da nicht mit beachtet werden.

Schorschi5566 5. Dez 2010 14:45

AW: EOutOfResources erzeugt Speicherleck
 
In diesem Fall bringen 30 gegenüber 10 Threads einen deutlichen Vorteil. Mit den Benchmarks bin ich schon durch. ;)

himitsu 5. Dez 2010 14:47

AW: EOutOfResources erzeugt Speicherleck
 
Zitat:

Zitat von Schorschi5566 (Beitrag 1066324)
In diesem Fall bringen 30 gegenüber 10 Threads einen deutlichen Vorteil. Mit den Benchmarks bin ich schon durch. ;)

Dann ist wohl deine Umrechnung der Bilder nicht sonderlich gut.

Schorschi5566 5. Dez 2010 14:58

AW: EOutOfResources erzeugt Speicherleck
 
Was hat das mit der Qualität des Downscalingfilters zu tun? (Ist der Lanczos-Filter aus Graphics32. BTW: Hervorragende Qualität)

Das Problem ist doch am Rande des Resourcenlimits zu arbeiten. Wenn ich vor dem Schritt, der die Exception verursacht, wüsste wie viel Speicher benötigt wird, könnte ich es ja abfangen und den Thread schon vorher abknipsen. Das weiß ich aber nicht und genau dazu ist Exceptionhandling doch da, oder?

P.S.: Das Teil verursacht wirklich keine Lecks mehr (mal von dem ganz kleinen abgesehen). Getestet über mehrere Stunden anhand von Taskmanager und Process Explorer. Die Handles nehmen nicht zu und der Speicherverbrauch steigt auch nicht an. Im Moment habe ich allerdings Probleme in Graphics32 die entsprechenden Stellen im Source zu finden, die für das Minileck verantwortlich sein könnten.

littleDave 5. Dez 2010 15:21

AW: EOutOfResources erzeugt Speicherleck
 
Die Exception EOutOfResources ist eine besondere Exception, da sie von der EOutOfMemory - Exception abgeleitet wird. Erstell mal ein Test-Projekt, binde FastMM ein und auf die Form1 klatscht du ein Button drauf. Im Click-Handler schreibst du folgendes:
Delphi-Quellcode:
procedure TForm1.Button1Click(Sender: TObject);
begin
  raise EOutOfResources.Create('bla bla blub');
end;
Dann starte das Testprogramm, klick den Button und beende das Programm: und siehe da: EOutOfResources wird nicht freigegeben. Wenn du jetzt irgend eine andere beliebige Exception (außer EOutOfMemory) benutzt, wird diese korrekt freigegeben.

Exceptions, die von EOutOfMemory abgeleitet sind, dürfen also nicht so geworfen werden. Für die EOutOfMemory gibt es in der SysUtils-Unit die Funktion
Delphi-Quellcode:
OutOfMemoryError
, ob es das für EOutOfResources auch gibt, weiß ich nicht.

Die Ausnahme ist sehr sinnvoll: wenn kein Speicherplatz mehr vorhanden ist, soll ja diese Exception geworfen werden. Blöd wäre jetzt nur, wenn für die Exception auch kein Speicher mehr vorhanden wäre. Daher wird eine Instanz der Exception global beim Start angelegt und diese Instanz wird einfach immer wieder neu geworfen.

[Ergänzung]
Mal ein Auszug aus der Delphi-Hilfe für EHeapException:
Zitat:

Die von EHeapException abgeleiteten Klassen EOutOfMemory und EInvalidPointer werden dazu verwendet, fehlgeschlagene Zuweisungen von dynamischem Speicher und ungültige Zeigeroperationen abzufangen.

Hinweis: Speicher für diese Exceptions wird im Voraus zugewiesen, sobald eine Anwendung startet. Die Zuweisung bleibt bestehen, solange die Anwendung läuft. EHeapException oder Nachkommen davon dürfen niemals direkt ausgelöst werden.

Schorschi5566 5. Dez 2010 15:59

AW: EOutOfResources erzeugt Speicherleck
 
Hallo littleDave,

vielen Dank für die Info. Darauf lässt sich aufbauen. :)


Grüße,
Schorsch

Bernhard Geyer 5. Dez 2010 16:35

AW: EOutOfResources erzeugt Speicherleck
 
Zitat:

Zitat von Schorschi5566 (Beitrag 1066320)
Wenn 30 Threads versuchen jeweils eine Grafik mit 5000x5000 Punkten in den Speicher zu laden, bekommt man die Meldung recht schnell. ;-)

Das Problem ist das jede Grafik ca. 100 MB an durchgehenden Adressraum benötigen (bei 32-Bit Farbtiefe). Da dein Adressraum (mit entsprechender Linker-Option) maximal 3 GB umfasst, aber Windows und diverse Anwendungen DLL's in deinen Adressraum "injezieren" ist 30*100MB eher schon Glück wenn es nicht permanent kracht.

Schorschi5566 5. Dez 2010 17:03

AW: EOutOfResources erzeugt Speicherleck
 
Das ist ja gar nicht das Problem. Klar kracht das (fast) permanent, wenn viele so große Grafiken verarbeitet werden. :D

Aber der Mechanismus bei Speichermangel die Threads zu killen und sie langsam wieder "auferstehen" zu lassen funktioniert ja. Es sind nicht nur derart große Grafiken, sondern auch viele mit kleineren Formaten. Da wäre es einfach doof, die Threads nicht wieder zu starten. Natürlich nicht alle auf einmal.

Die Leckage scheint wohl eher daher zu kommen, dass EOutOfResources irgendwo direkt geraised wird, was ja, laut Hilfe, für EHeapExceptions nicht erlaubt ist. Dabei entsteht pro Exception offenbar eine neue Instanz von EOutOfResources, was eigentlich nicht sein soll. Freigegeben wird aber nur die Urinstanz von EOutOfResources.

Mein Code feuert keine EOutOfResources, also muss es wohl TJpgImage, TBitmap oder TBitMap32 sein.

In meiner Exceptionbehandlung habe ich ja wohl kaum eine Chance, das zu beheben. Oder doch?

Bleibt die Frage, wie man korrekt auf EOutOfResources reagieren kann ohne eine Speicherleck zu erzeugen.

littleDave 5. Dez 2010 17:59

AW: EOutOfResources erzeugt Speicherleck
 
Soweit ich das gesehen habe, ist das ganze (in Delphi 7) nicht sauber umgesetzt, da es - nicht wie bei EOutOfMemory - keine Methode gibt, die das gleiche für die EOutOfResources-Exceptions macht (habe keine gefunden).

Erstmal grundlegend:
EHeapException hat eine private Boolean-Variable namens "AllowFree". Wenn diese True ist, wird die Instanz wirklich aus dem Speicher gelöscht, ansonsten nicht. Zur Prüfung dieser Variable überschreibt EHeapException die virtuelle TObject-Methode "FreeInstance" und baut dort die Prüfung ein (siehe SysUtils.pas -> EHeapException.FreeInstance).

Um das zu umgehen, hab ich eben eine Quick-and-Dirty-Lösung zusammengebaut

Delphi-Quellcode:
procedure TForm1.Button1Click(Sender: TObject);
var delta: cardinal;
begin
  try
    raise EOutOfResources.Create('Out of resources');
  except
    on e: EOutOfResources do
    begin
      delta := cardinal(EHeapException.ClassParent.InstanceSize); // den Offset zur Boolean-Variable auslesen
      PBoolean(cardinal(e) + delta)^ := True; // "AllowFree" auf "True" setzen (Offset + Instance-Pointer = Heap Pointer)

    end; // <-- hier wird die Exception dann automatisch freigegeben (siehe auch: AcquireExceptionObject, etc.)
  end;
end;
Das ganze muss natürlich etwas angepasst werden, falls in neueren Delphi-Versionen die Klasse "EHeapException" noch weitere Variablen hat.

himitsu 5. Dez 2010 19:26

AW: EOutOfResources erzeugt Speicherleck
 
EHeapException und Nachfahren, wie EOutOfMemory und EOutOfResources ruft man auch nicht direkt via Raise auf.

Diese sind ja nötig, wenn entsprechende Resourcen schon aufgebraucht sind und man diese Exceptions eventuell garnicht mehr erzeugen kann.

Für EOutOfMemory ruft man z.B. Delphi-Referenz durchsuchenOutOfMemoryError auf, welches das schon vorhandene globale Objekt nutzt.


EOutOfResources also direkt aufzurufen wäre der falsche Weg,
was demnach für einen fehlerhafte Nutzung in Delphi spricht.
Schon interessant, daß dieser Designfehler bis jetzt nicht so sehr aufgefallen ist. :shock:

Sir Rufo 5. Dez 2010 19:36

AW: EOutOfResources erzeugt Speicherleck
 
@himitsu das wurde doch schon hier erwähnt

himitsu 5. Dez 2010 20:24

AW: EOutOfResources erzeugt Speicherleck
 
Ich meinte von BorCodEmba entdeckt und behoben :angel:

Schorschi5566 5. Dez 2010 21:30

AW: EOutOfResources erzeugt Speicherleck
 
Hallo zusammen,

nochmals vielen Dank für die rege Beteiligung. :)

@littleDave: Klingt plausibel, funktioniert aber irgendwie nicht. Der Inhalt von PBoolean(cardinal(e) + delta)^ ist zwar False und wird dementsprechend auf True gesetzt, aber die Meldung vom Speichermanager (der bei Delphi2010 ja wohl FastMem ist) bleibt die gleiche.

@himitsu: Es geht ja nicht darum, dass ich die Exception auslösen, sondern dass ich darauf reagieren will. Mache ich ja auch, nur bleibt dabei eben die Exception übrig. :stupid:

littleDave 5. Dez 2010 21:41

AW: EOutOfResources erzeugt Speicherleck
 
Ah, du hast Delphi 2010, da musst du es so machen:

Delphi-Quellcode:
    on e: EOutOfResources do
    begin
      delta := cardinal(EHeapException.ClassParent.InstanceSize) - SizeOf(Pointer); // den Offset zur Boolean-Variable auslesen
      PBoolean(cardinal(e) + delta)^ := True; // "AllowFree" auf "True" setzen (Offset + Instance-Pointer = Heap Pointer)

    end; // <-- hier wird die Exception dann automatisch freigegeben (siehe auch: AcquireExceptionObject, etc.)
In Delphi 2010 wird an das Ende der Klasseninstanz noch eine TMonitor-Referenz gespeichert. Daher muss der Pointer auf diese Referenz vom Offset noch abgezogen werden.

himitsu 5. Dez 2010 21:46

AW: EOutOfResources erzeugt Speicherleck
 
Ich sag ja, ist ein Fehler in Delphi.
Diese Exception dürfte erst garnicht ständig neu erzeugt werden ... wie es richtig geht, sieht man an EOutOfMemory.
Aber man kann es eh nicht ordentlich implementieren, da AllowFree privat ist und somit in anderen Units nicht nutzbar ist.
(sowas gehört in protected)

Fazit: Dieses Singleton ist fehlerhaft implementiert und falsch verwendet.

littleDave 5. Dez 2010 21:50

AW: EOutOfResources erzeugt Speicherleck
 
Die EOutOfResources-Exception müsste im Constructor automatisch die "AllowFree"-Variable auf True setzen oder nicht von EHeapException abgeleitet sein. Diese Exception soll ja darauf hinweisen, dass keine Handles mehr zur Verfügung stehen - jedoch braucht eine Exception an sich kein Handle und sollte somit eigentlich ohne Probleme erstellt werden können (im Gegensatz zur EOutOfMemory-Exception) - auch wenn das System keine Handles mehr hat.

himitsu 5. Dez 2010 21:55

AW: EOutOfResources erzeugt Speicherleck
 
Auch EOutOfMemory ist schon fehlerhaft implementiert.

Bei diesen Exceptions müßten nicht nur die globalen Exceptionobjekte schon erstellt worden sein, sondern auch schon das ganze Meldungsfenster.

Bei EOutOfResources braucht man zwar für das Exceptionobjekt kein Handle, aber für das Fehlerfenster schon.

littleDave 5. Dez 2010 22:00

AW: EOutOfResources erzeugt Speicherleck
 
Als fehlerhaft würde ich das nicht bezeichnen, denn es muss ja nicht unbedingt ein Fester geben (Console-Anwending, Service, etc.). Daher finde ich diese pauschale Bezeichnung falsch. Zudem ist bei diesen Fehlern wichtig, dass man vorher nicht mehr benötigte Resourcen freigibt, bevor eine Meldung angezeigt wird.

himitsu 5. Dez 2010 22:04

AW: EOutOfResources erzeugt Speicherleck
 
EOutOfResources ist in Classes deklariert, wärend EHeapException in SysUtils deklariert ist.
Da AllowFree aber als Privat deklariert ist, kann man externe Nachkommen von EHeapException überhaupt nicht ordnungsgemäß verwenden.

littleDave 5. Dez 2010 22:07

AW: EOutOfResources erzeugt Speicherleck
 
Klar, die Variable gehört in protected - ich meinte eher, dass es falsch wäre, das Meldungsfenster gleich mit zu initialisieren.

Schorschi5566 5. Dez 2010 22:15

AW: EOutOfResources erzeugt Speicherleck
 
So, jetzt ist die Meldung weg. Super und nochmals danke. :)

Warum überhaupt eine EOutOfResources kommt, ist mir zwar immer noch nicht wirklich klar aber zumindest bleiben die Exceptions jetzt nicht mehr im Speicher hängen.

In meinem Fall werden doch nur einige GDI-Handles angefordert. Wird der dafür nötige Speicher ebenfalls gleich mitbestellt (dann wäre es logisch) oder wie muss man sich das vorstellen?

Das Programm hat zur Laufzeit zwischen 100-120 der GDI-Handles, Tendenz gleichbleibend.

Luckie 6. Dez 2010 00:26

AW: EOutOfResources erzeugt Speicherleck
 
Also ich halte es immer noch für falsch bewusst in eine Exception reinzulaufen. Ich meine, ich lasse Autos ja auch nicht bewusst an einer Ampel Unfälle bauen und stelle einen Abschleppwagen da neben, um die Wracks weg zu räumen.

Bummi 6. Dez 2010 07:56

AW: EOutOfResources erzeugt Speicherleck
 
@Luckie
Du bist nicht der Einzige :cheers:
ist trotzdem interessant dieses Thema zu verfolgen.

Schorschi5566 6. Dez 2010 09:08

AW: EOutOfResources erzeugt Speicherleck
 
Zitat:

Zitat von Luckie (Beitrag 1066428)
Ich meine, ich lasse Autos ja auch nicht bewusst an einer Ampel Unfälle bauen und stelle einen Abschleppwagen da neben, um die Wracks weg zu räumen.

Na ja, das sind dann die, die 10 Meter vor der Ampel anhalten. :mrgreen:


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