![]() |
EOutOfResources - Wie viele Handles sind noch verfügbar?
Hallo zusammen.
Ich habe das Problem, dass mein Programm nach mehreren Stunden kommentarlos zugeht. Nach Beseitigung div. technischer Probleme, die für den Thread irrelevant sind, konnte ich das Programm nun endlich in Delphi / mit Debugger lange genug laufen lassen, um den Fehler angezeigt zu bekommen. Leider war das Programm incl. Delphi soweit aufgehängt, dass das Kopieren der Nachricht nicht möglich war. Das wichtigste konnte ich aber gerade noch ablesen: Es war -in diesem konkreten Fall- eine EOutOfResources Exception, die auslösende Prozedur konnte er mir auch gerade noch anzeigen. Falls dies in allen Fällen, in den das Programm kommentarlos zugegangen ist, die Ursache war, besteht ja Chance auf Besserung :stupid:. Der Fehler konnte auch schnell gefunden werden; durch eine falsche Verschachtelung wurde ein mit GetDC erhaltenes Handle nicht wieder freigegeben, und diese Prozedur wird aktuell ~4x Sekunde aufgerufen, also kommen pro Stunde knapp 10'000 neue Handles dazu, die nicht freigegeben werden - klar, dass das System irgendwann streikt. Ich glaube, auch ein zweites TBitmap wurde nicht wieder freigegeben. Was mich nun akut irritiert: -Im Taskmanager waren CPU (hierfür eh irrelevant) und RAM auf Bestwerten. Das Programm verbraucht je nach Arbeitsschritt mal mehr, mal weniger RAM, pendelt sich aber immer wieder bei ~15 MB ein. Also ist die Resource "RAM" schon mal nicht aufgebraucht. -Ich hatte bis dato immer regelmäßig
Delphi-Quellcode:
aufgerufen. Evtl. sorgte das dafür, dass zwar der RAM von dem vergessenen TBitmap wieder freigegeben wurde und ich es deshalb im Taskmanager nicht als steigender Ram-Verbrauch gesehen habe...
CoFreeUnusedLibraries;
SetProcessWorkingSetSize(GetCurrentProcess, $FFFFFFFF, $FFFFFFFF);// DefragMem; -Wahrscheinlich bekomme ich gleich von euch einen freundlichen Hinweis, das "Handle"s nichts (direkt) mit RAM zu tun haben, und man die genutzten/"verbrauchten" Handles entsprechend nirgendwo sehen kann. Wäre irgendwo logisch. Die "Handles" im Taskmanager bei "Leistung" - "CPU" lassen übrigens keinen großen Rückschluss darauf zu, ob/dass mein Programm viele Handles verbraucht. Die Werte schwanken so stark durch Windows-Hintergrundprozesse, dass eine klare Aussage nicht möglich ist, was ich damit zu tun habe. Eine klar Zunahme von 10'000 Handles die Stunde ist auf jeden Fall nicht feststellbar... Bei "Resourcenmonitor" - "CPU" - [meine .exe] - "Zugeordnete Handles" ist die Liste auch mehrere Minuten nach dem Start noch unverändert... -EDIT: Ich hatte EurekaLog 7.0.7 in meinem Programm mitlaufen - Das Springt leider bei dem Fehler nicht an. Wahrscheinlich, weil es zum Erzeugen der Fehlermeldung ein freies Handle braucht. Meine Fragen zu dem Thema: 1) (Wie) kann ich -mit meinem Programm, oder: -mit Windows auslesen, wie viele Handles (von meinem Programm!) benutzt wurden, oder alternativ: noch zur Verfügung stehen. Ich könnte mir vorstellen, dass ich in dieser einen Prozedur zwar irgendwann bei "GetDC()" "0" erhalte (--> fehlgeschlagen) und dann wissen würde, dass es ab /jetzt/ einen Fehler gibt, weil keine freien Handles mehr verfügbar sind (bzw. dann würde wohl eher die o.g. Exception ausgelöst), aber ich würde gerne "Überwachungsmäßig" über die ganze Programmlaufzeit beobachten, ob/wie sich mein Handleverbrauch entwickelt, und ob der aktuelle Trend vermuten lässt, dass das Programm bald irgendwann mangels freier Handles abstürzen wird. Oder auch für meine Logging-Funktion: freie Handles vor/nach Durchlauf der Prozedur 2) Eine Bitte: Schaut euch die nachfolgende Prozedur an, und sagt mir, ob in der jetzigen, korrigierten Form Bedenken für Speicherverbrauch oder nicht freigegebene Handles bestehen:
Delphi-Quellcode:
Vielen Dank :thumb:
// Prüft, ob der Inhalt von _Parent komplett Schwarz ist
// Die Prozedur zeichnet, FALLS übergeben, auf _TargetBitmap, was sie "gesehen" hat // Zum Vermeidung div. Probleme wird der untersuchte Bereich auf 640x480 px skaliert, // das ist für diese Zwecke mehr als ausreichend genau function EndOfPresentation_Neu(_Parent: TWinControl; _TargetBitmap: Graphics.TBitmap): Boolean; var myBitmap: graphics.TBitmap; DetectHeight, DetectWidth: Integer; SourceDC: HDC; hwParentHandle: HWND; const CAPTUREBLT = $40000000; SCAN_WIDTH = 640; SCAN_HEIGHT = 480; begin Result := False; // Variablen auf gültige Werte initialisieren, damit sie // auf jeden Fall korrekt freigegeben werden können hwParentHandle := 0; myBitmap := NIL; try // Kein Parent, keine Analyse if not assigned(_Parent) then Exit; if not _Parent.InheritsFrom(TWinControl) then Exit; DetectWidth := _Parent.Width; DetectHeight := _Parent.Height; if (DetectHeight < 1) or (DetectWidth < 1) then Exit; // TBitmap initialisieren, und auf die erwartete Größe bringen try hwParentHandle := _Parent.Handle; myBitmap := Graphics.TBitmap.Create; myBitmap.PixelFormat := pf32bit; // Zu analysierender Bereich ist lediglich 640 x 480 px myBitmap.Width := SCAN_WIDTH; myBitmap.Height := SCAN_HEIGHT; except on E: Exception do begin log('EndOfPresentationDetector.EndOfPresentation_Neu.InitializeValues.Exception: ' + E.Message); end; end; // Screenshot vom betroffenen Bereich erstellen if assigned(myBitmap) then begin try SourceDC := GetDC(hwParentHandle); if SourceDC <> 0 then begin // das "_Parent"-Objekt abfotografieren. Wir skalieren gleichzeitig auf 640 x 480. Hinreichend genau für unseren Zweck. StretchBlt(myBitmap.Canvas.Handle, 0, 0, SCAN_WIDTH, SCAN_HEIGHT, SourceDC, 0, 0, DetectWidth, DetectHeight, SRCCOPY or CAPTUREBLT); ReleaseDC(hwParentHandle, SourceDC); end; except on E: Exception do begin log('EndOfPresentationDetector.EndOfPresentation_Neu.DoBitBlt.Exception: ' + E.Message); end; end; try // Prozedur, die nur auf die Scanlines von myBitmap zugreift, und zurückgibt, Keine Leak/Handle Bedenken! // Gibt an wie viele Prozent des Bildes explizit schwarz sind, // bzw. bei wieviel Prozent der Höhes des Bildes (von Oben gesehen) // die letzte Zeile mit mind. einem nicht-schwarzen Pixel ist Result := (ObereKanteNurSchwarzInProzent(myBitmap) < 25); // Falls der Parameter übergeben wurde, wird auf _TargetBitmap das erfasste, nun skalierte und mit // Hilfslininen versehene Bild ausgegeben, so dass es dem Benutzer angezeigt werden könnte if assigned(_TargetBitmap) then begin _TargetBitmap.Assign(myBitmap); end; except end; FreeAndNil(myBitmap); end; except on E: Exception do begin log('EndOfPresentationDetector.EndOfPresentation_Neu.Global.Exception: ' + E.Message); end; end; end; |
AW: EOutOfResources - Wie viele Handles sind noch verfügbar?
Du solltest da unbedingt mit try/finally arbeiten. Wenn du da irgendwo ausserhalb deiner unnötigen Exceptions eine Exception bekommst, hast du danach zumindest für das Bitmap ein Memoryleak. Dass du da so viele try/excepts drin hast macht keinen Sinn. So wie du das da machst reicht einer um den ganze Code. Oder willst du wirklich, dass Teile deines Codes noch ausgeführt werden, wenn davor eine Exception aufgetreten ist?
Bitte schütze deine erstellten Resourcen mit einem try/finally und alles ist bestens. Sobald du ein Objekt erstellst, kommt immer ein try und am Ende ein finally in dem du das Objket wieder frei gibst. Wenn du dich daran hälst wirst du nie mehr Probleme dieser Art haben. Für ein Tool, das dir die allozierten Handels und noch viel mehr zeigt, emfehle ich dir den Prozess Explorer von SysInternals (Microsoft). ![]()
Delphi-Quellcode:
// Prüft, ob der Inhalt von _Parent komplett Schwarz ist
// Die Prozedur zeichnet, FALLS übergeben, auf _TargetBitmap, was sie "gesehen" hat // Zum Vermeidung div. Probleme wird der untersuchte Bereich auf 640x480 px skaliert, // das ist für diese Zwecke mehr als ausreichend genau function EndOfPresentation_Neu(_Parent: TWinControl; _TargetBitmap: Graphics.TBitmap): Boolean; var myBitmap: graphics.TBitmap; DetectHeight, DetectWidth: Integer; SourceDC: HDC; State: String; hwParentHandle: HWND; const CAPTUREBLT = $40000000; SCAN_WIDTH = 640; SCAN_HEIGHT = 480; begin Result := False; // Variablen auf gültige Werte initialisieren, damit sie // auf jeden Fall korrekt freigegeben werden können hwParentHandle := 0; myBitmap := NIL; try // Kein Parent, keine Analyse if not assigned(_Parent) then Exit; if not _Parent.InheritsFrom(TWinControl) then Exit; DetectWidth := _Parent.Width; DetectHeight := _Parent.Height; if (DetectHeight < 1) or (DetectWidth < 1) then Exit; // TBitmap initialisieren, und auf die erwartete Größe bringen State := 'EndOfPresentationDetector.EndOfPresentation_Neu.InitializeValues.Exception: '; hwParentHandle := _Parent.Handle; myBitmap := Graphics.TBitmap.Create; try myBitmap.PixelFormat := pf32bit; // Zu analysierender Bereich ist lediglich 640 x 480 px myBitmap.Width := SCAN_WIDTH; myBitmap.Height := SCAN_HEIGHT; // Screenshot vom betroffenen Bereich erstellen State := 'EndOfPresentationDetector.EndOfPresentation_Neu.DoBitBlt.Exception: '; SourceDC := GetDC(hwParentHandle); if SourceDC <> 0 then begin try // Dieses try/finally ist eigentlch überflüssig, da es hier keine Exception geben sollte // das "_Parent"-Objekt abfotografieren. Wir skalieren gleichzeitig auf 640 x 480. Hinreichend genau für unseren Zweck. StretchBlt(myBitmap.Canvas.Handle, 0, 0, SCAN_WIDTH, SCAN_HEIGHT, SourceDC, 0, 0, DetectWidth, DetectHeight, SRCCOPY or CAPTUREBLT); finally ReleaseDC(hwParentHandle, SourceDC); end; end; State := 'EndOfPresentationDetector.EndOfPresentation_Neu.ObereKanteNurSchwarzInProzent.Exception: '; // Prozedur, die nur auf die Scanlines von myBitmap zugreift, und zurückgibt, Keine Leak/Handle Bedenken! // Gibt an wie viele Prozent des Bildes explizit schwarz sind, // bzw. bei wieviel Prozent der Höhes des Bildes (von Oben gesehen) // die letzte Zeile mit mind. einem nicht-schwarzen Pixel ist Result := (ObereKanteNurSchwarzInProzent(myBitmap) < 25); // Falls der Parameter übergeben wurde, wird auf _TargetBitmap das erfasste, nun skalierte und mit // Hilfslininen versehene Bild ausgegeben, so dass es dem Benutzer angezeigt werden könnte if assigned(_TargetBitmap) then begin _TargetBitmap.Assign(myBitmap); end; finally FreeAndNil(myBitmap); // wird so immer freigeben, auch im Fall einer Exception end; except on E: Exception do begin log(State + E.Message); end; end; end; |
AW: EOutOfResources - Wie viele Handles sind noch verfügbar?
|
AW: EOutOfResources - Wie viele Handles sind noch verfügbar?
Hallo,
vielen Dank euch Beiden für die Rückmeldungen. Die Tools TiGü belegen direkt, dass meine alte Version tatsächlich mit den GDI-Handels nach oben geht. Zwar "nur" ca. 10 pro Minuten, aber mit entsprechend langer Laufzeit führt das unweigerlich zu der o.g. Exception. Der ProcessExplorer von Rolf ist ja auch von Sysinternals und bekommt das bestimmt auch hin. Danke auch Dir für diesen Tip. Bzgl. der Exceptions: Manchmal sieht man echt den Wald vor lauter Bäumen nicht. Ich war bis dato -weshalb auch immer- der Meinung, dass alle Exceptions, die nicht unmittelbar mit try..except abgefangen werden, an das GUI eskaliert werden: deshalb fange ich an jeder möglichen Stelle eben genau dies so ab. try..finally war für mich bis dato die unnützeste Funktion in ganz Delphi, weil: alles, was nach einer Exception gemacht werden muss, schreibe ich einfach nach try..except, denn "diese Zeile wird ja auch nach der Exception ausgeführt". In meinem Beispielcode müsste -in genau dieser Konstellation- myBitmap stets freigegeben werden, da alle vorherigen Exceptions (ja: unnötig oft) abgefangen und zumindest mit Logbuch behandelt werden. Zwar kann FreeAndNil(myBitmap) hier nur aufgerufen werden, wenn dieses vorher "Assigned()" war, aber wenn es nicht assigned war, muss es ja auch nicht freigegeben werden, oder? Ich will hier nicht in den Krümeln suchen, nur der Vollständigkeit halber helft mir bitte es zu verstehen, falls ich hier falsch liege. Rolfs Variante ist unabhängig davon die Bessere, die ich auch zukünftig verwenden werde. Die Kritik an meinem ReleaseDC ist zu 100% berechtigt. Deinen Ansatz, Rolf, mit try..finally *umgeben* von try..except habe ich mir eben mir einer kleinen Demo reingezogen:
Delphi-Quellcode:
Dadurch, dass die Exception *nicht* ans GUI eskaliert wird (das dachte ich früher wohl nur, weil ich damals try..finally nicht von try..except nochmal umgeben hatte, und es in diesem Fall tatsächlich ans GUI ging), sondern erst die Finally behandlung stattfindet und *dann* die Exception-Behandlung ist das natürlich des wesentlich bessere Weg. Es ist immer wieder erschreckend, dass auch nach so vielen Jahren Delphi-Erfahrung solche Grundlagen erst tatsächlich "verstanden" werden. Schämschäm. Aber besser spät als nie.
procedure TfrmDemo1.Button1Click(Sender: TObject);
var tmp: TButton; begin try try tmp := NIL; tmp.Click; finally ShowMessage('finally'); end; ShowMessage('2'); except ShowMessage('except'); end; end; Danke nochmal! |
AW: EOutOfResources - Wie viele Handles sind noch verfügbar?
Das try/finally ist eigentlich eines der wichtigsten Konstrukte und du solltest dir angewöhnene damit zu arbeiten. Deine Variante mit den vielen try/except würde zwar auch funktionieren, aber ist meiner Meinung nach viel zu kompliziert. Ausserdem ist es nicht unbedingt die fein Art da einfach alle Exception zu "schlucken". Wie weist du denn ausserhalb deiner Routine, ob alles sauber verabreitet wurde? Wertest du da nach jedem Aufruf das Log aus? Das kann ich mir nicht vorstellen. Ich mache für solche Situationen in der Regel auch eigene Exceptions und frage die dann extern ab, damit ich weiss was da genau passiert ist und wie ich auf die jeweilige Situation reagieren muss.
OK in deinem Beispiel mit dem Button1Click Event macht das keinen Sinn, da du das nicht selber aufrufst, aber in einer Funtkion die du an verschiedenen Stellen aufrufst, ist das eine dumme Idee einfach alle Exceptions zu verschlucken. Hier nochmal ein kleines Beispiel, wenn das Bestandteil einer Funktion wäre. Ob du das except oder das finally zu oberst hast, hängt vom jeweiligen Code ab. Du bist da eigentlich frei wie du das machst. Der Auswertung erfolgt immer vom innersten Block nach aussen. Also hier wird zuerst das Except behandelt und erst danach das Finally. In deinem Beisiel ist es umgekehrt, weil da das Finally zu innerst ist.
Delphi-Quellcode:
type
EMyNilException = class(Exception); procedure DoSomething(); var tmp: TButton; begin try try tmp := NIL; if tmp = nil then raise EMyNilException.Create('tmp ist NIL!'); tmp.Click; except on E: EMyNilException do begin ShowMessage(e.Message); raise; end else raise; // Exception die wir nicht selber behandeln sollten erneut ausgelöst werden. end; ShowMessage('2'); finally ShowMessage('finally'); end; end; begin try DoSoemthing; except on E: EMyNilException do ShowMessage('Es ist eine NIL-Verletzung aufgetreten'); else raise; end; end |
Alle Zeitangaben in WEZ +1. Es ist jetzt 17:14 Uhr. |
Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024-2025 by Thomas Breitkreuz