Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Sonstige Fragen zu Delphi (https://www.delphipraxis.net/19-sonstige-fragen-zu-delphi/)
-   -   Delphi Problem mit Speicher und Handle (https://www.delphipraxis.net/110177-problem-mit-speicher-und-handle.html)

s.h.a.r.k 14. Mär 2008 08:30


Problem mit Speicher und Handle
 
Hallo,

und zwar habe ich im Moment ein seltsames Problem und ich komme nicht ganz drauf, wieso. Ich habe eine Anwendung und eine passende objektorientierte Datenschicht. Ich greife auf eine Oracle-Datenbank (Version 9.x) mit AnyDAC zu und baue somit die Datenstruktur im Hintergrund auf. Diese kann ich natürlich immer wieder erneuern (=> refresh). Die Anzeige der Daten handhabe ich mit einem VirtualDrawTree (Virutal Treeview)).

Nun ist es so, dass ich nach mehrmaligem aktualisieren der Daten den folgenden Fehler erhalte:
Code:
Im Projekt panda.exe ist eine Exception der Klasse EOSError mit der Meldung 'Systemfehler. Code: 1158.
Der aktuelle Prozess verwendet alle Handles der zulässigen Höchstanzahl für Window Managerobjekte' aufgetreten.
Ich beobachte auch, dass meine Anwendung nach dem Start ca 36MB (!!!) für sich beansprucht. Der Verlauf weiterhin schaut dann wie folgt aus:
Aktualisierung
1. -> 36MB
2. -> 38MB
3. -> 46MB
4. -> 48MB
5. -> 59MB
x. -> 59MB

Ich weiß leider nich genau, wo der Speicher "abhanden" kommt bzw nicht mehr freigegeben wird. Es wäre auch ein wenig viel Quelltext die komplette Datenstruktur als Code hier zu präsentieren. Fakt ist aber, dass genau nach der Datenbankabfrage der Speicher immer und immer steigt, d.h. ich denke, dass es an AnyDAC liegt, wobei ich es eben nicht verstehe. So schaut mein Code aus. Der Speicher müsste doch wieder freigegeben werden und dürfte doch nicht mehr werden?!
Delphi-Quellcode:
// Beispielcode
sql := 'SELECT * FROM test_table';
table := TADDatSTable.Create();
try
  // gekapselte Methode aufrufen
  if (FDatabase.execSQL(sql, table) then
    // do something
  else
    // do nothing...
finally
  FreeAndNil(table);
end;
Ich hoffe ihr könnt mir hier weiterhelfen!

Mit freundlichen Grüßen
der Hai ;)

sirius 14. Mär 2008 08:41

Re: Problem mit Speicher und Handle
 
Es liegt erstmal nicht zwingend einen deinen Objekten.

Irgendwo in deinem Code oder in den verwendeten Komponenten oder aber auch in einem anderen laufenden Programm werden zu viele Handles (Pipe, Socket, Window, GDI, Event, Process....) verwendet.

Zitat:

The total number of open handles in the operating system is limited only by the amount of memory available to the operating system. However, a single process may have no more than 16,384 open GDI object handles. The per-process limit on kernel handles is 2^30. There is no per-process limit on user handles, but there is a systemwide limit of 65,536.
Aber wie man das rausbekommt, weiß ich grad nicht.

s.h.a.r.k 14. Mär 2008 09:07

Re: Problem mit Speicher und Handle
 
Jetzt stellt sich für mich die Frage, ob der verwendete Speicherplatz eben etwas mit den verwendeten Handles zu tun hat. Ich habe nun auch das folgende herausgefunden:

Delphi-Quellcode:
var
  FCommand : TADCommand;
  table : TADDatSTable;
begin
  // ...
  table := TADDatSTable.Create;
  try
    // SQL-Abfrage
    FCommand.Open;
    FCommand.Define(table);
    FCommand.Fetch(table, True); // !!! Genau nach der Zeile "explodiert"
                                  // die Speichernutzung von 15MB auf 44MB
    FCommand.Close;
    // ...
  finally
    FreeAndNil(table);           // ab hier sinkt Sie wieder auf 36MB
  end;
end;
Das Problem bei mir ist wohl, dass irgendwo Handles "verloren" gehen... Oder seh ich das falsch?

sirius 14. Mär 2008 10:00

Re: Problem mit Speicher und Handle
 
Ja, anscheinend fehlen irgendwo ein paar "CloseHandle"

Deinen Speicher (wie mißt du den überhaupt) dürfte das nicht direkt betreffen. Hinter den Handles stehen ja "Objekte" von Windows. Und letztenendes geht es ja bei der Fehlermeldung anscheinend nicht um die Größe, sondern um die Anzahl.

Dein Fetsch wird wahrscheinlich Daten aus der Datenbank in deinen Adressbereich (zu table) kopieren und hinterher gibt der Speichermanager nicht gleich alles wieder frei (also nicht an Windows zurück). Das ist ganz normal und unbedenklich.

s.h.a.r.k 14. Mär 2008 10:26

Re: Problem mit Speicher und Handle
 
Ich schaue im Taskmanager nach, wie viel Speicher meine Anwendung belegt. Ich weiß, dass dieser das nicht immer unbedingt richtig anzeigt (bestes Beispiel: BDS), aber ich denke, dass es schon eine Richtlinie ist, die einigermaßen passt. Das mit dem Speicher, der nicht an Windows zurückgegeben ist, kenne ich und ist mir klar. Nur dachte ich bisher dass das etwas mit dem Handle-Fehler zu tun hat.

Nun stellt sich mir die Frage, ob nicht AnyDAC einen internen Bug hat oder ich nur etwas falsch mache :gruebel:

Rein für mein Verständis:
Wenn ich ein Object (TMeinObjekt, ist von TObject abgeleitet) erzeuge, dann zeigt ja ein Handle auf die Instanz davon. Wenn ich FreeAndNil() aufrufe wird die Instanz "gelöscht" (also, aus dem Speicher entfernt) und das Handle automatisch freigegeben, oder?
Delphi-Quellcode:
var
  obj : TMeinObjekt;
begin
  // Objekt(e) erzeugen
  obj := TMeinObjekt.Create();

  // Berechnungen, etc.
  obj.doSomething;

  // Objekt freigeben
  FreeAndNil(obj);
end
Oder wo genau wird ein Handle erzeugt bzw. gebraucht? Dass es eine systemweite ID ist für z.B. Anwendungen weiß ich.

Danke schon mal für deine Hilfe, bringst mich immer ein Stück weiter.

Medium 14. Mär 2008 10:30

Re: Problem mit Speicher und Handle
 
Einfache Instanzen bekommen kein Handle, sondern nur solche Elemente, die in irgendeiner Weise von Windows bereitgestellt oder verwaltet werden. Dazu zählen z.B. Fenster (auch Buttons und Edits usw. sind so gesehen Fenster) und ein paar andere Dinge. Einfache Objekte aber haben da keinen Anteil dran.

s.h.a.r.k 14. Mär 2008 10:33

Re: Problem mit Speicher und Handle
 
Hm, dann versteh ich aber nicht ganz, warum mir keine Handles mehr zur Verfügung stehen... Ich mache nur Datenbankabfragen. Klar habe ich eine Main-Form, allerdings sind da nicht all zu viele Komponenten drauf.

sirius 14. Mär 2008 12:07

Re: Problem mit Speicher und Handle
 
Wenn du in Delphi ein Objekt erzeugst, bekommst kein Handle im Windows-Sinn, sondern einfach nur einen Instanzzeiger. Und der hat mit deinem Handleproblem nichts zu tun.

Wenn du ein "Objekt" (ein richtiges Objekt ist es nicht, aber so ähnlich) von Windows erzeugst, bekommst du ein Handle. Mit diesme Handle kannst du unter Windows spezielle funktionen ausführen. Beispielsweise bekommst du für einen Socket ein Handle. Dann kannst du Daten über dieses Socket (welches durch ein Handle identifiziert wird) ins Internet schicken.
Die Meisten Funktionen unter Windows laufen über Handles:
-Dateiarbeit
-Sockets
-Fenster
-Pipes
-Events
-Mutex/Semaphore
-Processe haben auch Handles
-Threads
Hinter jedem Handle in Windows verbirgt sich auch eine Struktur an Daten. Und je nach Typ des Handles kannst du bestimmte Funktionen mit dieser Struktur ausführen. Die Struktur (also die Daten) selber landen IHMO aber bei Windows und nicht in deinem Speicherbereich.

Deine Vermutung würde ich auch unterstützen. Da schein ein Bug in dieser Komponente zu sein. Versuch mal die Methode der Komponente heruaszufinden um den Fehler (allein mit dieser Methode; durch x-mal aufrufen bspw.) zu reproduzieren.


PS: Auch ich habe nur Vermutungen, aber etwas anderes fällt mir dazu nicht ein. Den Fehler selber hatte ich auch noch nie.

s.h.a.r.k 14. Mär 2008 12:29

Re: Problem mit Speicher und Handle
 
Liste der Anhänge anzeigen (Anzahl: 1)
Nach einigem an Testen habe ich den Fehler gefunden. Und zwar hole ich dazu etwas weiter aus:

Im Taskmanager hat man die Möglichkeit sich mehrere Spalten anzeigen zu lassen, unter dem Tab Prozesse. Dort habe ich mir, wie im Screenshot (siehe Anlage) die Handles, Thread und Objekte, die eine Anwendung zurzeit benötigt anzeigen lassen. Nun bin ich dann mit dem Debugger durch meine Anwendung gegangen und habe nach dem Problem selbst gesucht, d.h. an welches Stelle nun das Problem auftritt. Hierbei war sehr auffällig, dass die Benutzerobjekte einen sehr großen Teil ausgemacht hatten. Und zwar handelte es sich bei mir um einen Timer. Dieser wurde beim Konstruktor eines Objekts erzeugt, dieser erhält auch ein Handle! Bei genau 10000 Timern, d.h. 10000 Benutzerobjekten war dann Schluss und es wurde die Exception geworfen. Die Handle-Zahl stieg dabei allerding nicht! (laut Task-Manager)

Mein Problem hierbei war das folgende: ich wollte eine MDI-Anwendung erstellen, d.h. für jedes Objekt sollte es möglich sein, ein MDI-Formular aufrufen zu lassen. Per eingebauten Timer sollte sich das Objekt, welches einem Datensatz in der Datenbank zugeordnet ist, dann sperren, d.h. sodass kein anderer User daran Daten ändern kann. Dies mache ich, indem ich alle 20 Sekunden einen Zeitstempel in die DB schreibe und diesen dann bei einem anderen User, welcher auf den Datensatz zugreift abfrage. Nun habe ich, wie schon erwähnt den Timer beim Konstruktor erzeugt und dann eben beim Öffnen des MDIs nur gestartet, ergo war das Timer-Objekt immer vorhanden. Dies hätte dann dazu geführt, das ab 10000 Datensätzen mein Programm abschmiert. Durch einen weiteren dummen Programmierfehler (ich habe die Timer nicht an der passenden Stelle freigegeben) bin ich dann erst auf das Problem gestoßen. Nun setze ich nicht mehr lediglich den Timer auf Enabled, sondern erzeuge ihn erst beim Erstellen des MDI-Forumlars und beschränkte die maximale Anzahl der geöffneten MDI-Forumlare, sodass ich dieses Problem umgehen kann. Eigentlich brauch ich keine Beschränkung, da 10000 eine recht große Zahl ist, aber ich habe ja noch andere Elemente, die auch Platz brauchen ;)

Ich hoffe, ich habe nun alles passend erklärt! Wenn es noch Fragen oder Unklarheiten geben sollte, dann bitte melden.

PS: Wer Rechtschreibfehler findet darf sie behalten :P

[EDIT] Bild vergessen :wall: [/EDIT]

Mit freundlichen Grüßen
der Hai

Medium 14. Mär 2008 12:57

Re: Problem mit Speicher und Handle
 
Es gibt imho entweder eine praktikable, oder sogar definierte Obergrenze an Timern die registrierbar sind. 10000 halte ich schon für überirdisch! Du solltest das Konzept überdenken, z.B. nicht für jedes Formular einen eigenen Timer, sondern nur einen für alle. Offene Formulare tragen sich in eine Liste ein, und der Timer geht die Liste durch und macht die DB Einträge. So dürfte ein Schuh draus werden. Aber 10k Timer sind... boah :lol:

Nuclear-Ping 14. Mär 2008 13:18

Re: Problem mit Speicher und Handle
 
Es empfiehlt sich der Einsatz eines Leak-Reporters, wie FastMM oder MemCheck mit den entsprechenden Einstellungen, der dir nach Beenden deiner Anwendung die meisten Leaks zeigt.

stahli 4. Mär 2010 16:39

Re: Problem mit Speicher und Handle
 
Liste der Anhänge anzeigen (Anzahl: 1)
Hallo Armin,

danke für Deinen Hinweis! Hat mir gerade sehr gehofen.
Von mir noch eine Anmerkung:

Ich habe Datenobjekte erstellt, die grötenteils aus mehreren Panels und Labels bestehen und Datenbankinhalte darstellen.
Darüber hinaus gibt es Listenobjekte (von der Scrollbox abgeleitet), die Mengen von Datenkomponenten anzeigen und in Spalten und/oder Zeilen anordnen.
Die Listenobjekte können auch ihre eigene Größe anpassen, um den Inhalt möglichst komplett anzuzeigen.
Jeder Eintrag in einer Listenkomponente kann selbst wieder Datenmengen anzeigen und seine Größe selbst danach ausrichten.
(Im Bild werden die Runden untereinander angeordnet und beinhalten wiederum Spiele, die dann in Spalten und notfalls in mehreren Zeilen angeordnet werden.)

Da sind natürlich jede Menge Objekte verbraten und die Ausrichtung aller Datenkomponenten dauert etwas.
Nun hatte ich immer mal sporadisch den o.g. Handle-Fehler.

Dank Deinem Hinweis bin ich dem etwas näher auf die Schliche gekommen:
Wenn der Aufbau der Runden (im Bild) fertig war (Benutzerobjekte=2500), konnte ich "vorsichtig" durch die Runden scrollen. Das heisst, "Stück für Stück" - ein Stückchen scrollen, dann alles schön fertig zeichnen lassen, wieder ein Stückchen scrollen... Soweit kein Problem.
Wenn ich aber die Scrollbar voll "durchgezogen" habe, wurde alles normal gezeichnet, die Benutzerobjekte stiegen danach aber kontinuierlich immer weiter bis 10.000 und dann kam der Fehler.

Ich habe mir jetzt so beholfen, dass ich in der Listenkomponente (wenn eine verschachtelte Höhenanpassung erfolgen muss) jeden neuen Eintrag sofort zeichnen lasse.
Delphi-Quellcode:
if (ItemCreateFlag) and (ItemsAutoHeight or ItemsAutoWidth) then
begin
  ScrollInView(dcPanel);
  Application.ProcessMessages; // nur dieses allein half nicht
end;
Das hat zwar zur Folge, dass der Aufbau einer neuen Listenkomponente (bzw. neuen Inhalten) etwas unruhiger ist, aber der Fehler wird so vermieden.
Scheinbar verzettelt sich Windows, wenn wenn es später Komponenten darstellen soll, mit denen es sich noch nicht richtig auskennt ;-)

Zwei Fragen:
1) Kann man die Anzahl der verbrauchten Benutzerobjekte im eigenen Programm ermitteln?
2) Hat jemand eine Idee für eine bessere Alternative zu dem ScrollInView?


Stahli

s.h.a.r.k 8. Mär 2010 15:29

Re: Problem mit Speicher und Handle
 
Grüß dich :) Pro Frage einen Thread. Deine Fragen haben ja nicht direkt etwas mit meiner zu tun. Hätte ich nicht durch Zufall den Thread geöffnet hätte ich deine Frage nie gefunden :zwinker:


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