Delphi-PRAXiS
Seite 1 von 2  1 2      

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:


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