AGB  ·  Datenschutz  ·  Impressum  







Anmelden
Nützliche Links
Registrieren
Zurück Delphi-PRAXiS Programmierung allgemein GUI-Design mit VCL / FireMonkey / Common Controls Delphi Systemressourcen erschöpft beim VirtualStringTree.AfterCellPaint
Thema durchsuchen
Ansicht
Themen-Optionen

Systemressourcen erschöpft beim VirtualStringTree.AfterCellPaint

Ein Thema von Hobbycoder · begonnen am 15. Nov 2018 · letzter Beitrag vom 17. Nov 2018
Antwort Antwort
Hobbycoder

Registriert seit: 22. Feb 2017
1.017 Beiträge
 
#1

AW: Systemressourcen erschöpft beim VirtualStringTree.AfterCellPaint

  Alt 16. Nov 2018, 08:17
Leider liefern die mir auch (nicht wenige) MemoryLeaks von UniDAC, zu denen ich aber keine Sources habe.
Wir haben auch Unidac und nutzen auch FastMM und uns ist bisher kein MemoryLeak von Unidac (Oracle + MySQL) aufgefallen.
Gut. Aber wenn ich meine Anwendung kurz starte, dann bekomme ich eine ganz Liste (ReportMemoryLeaksOnShutDown, FastMM muss ich noch mal ausprobieren für mehr Details).
Und da mach ich dann noch nicht mal viel. Die Connection wird geöffnet, es werden 2-3 Query abgefragt, 2 Threads laufen durch. Das wars. Da sind ein paar Einträge, über deren Klassennamen kann meine Klassen erkennen. Das meiste stammt von UniDAC.
Ich will aber nicht ausschließen, dass es mein Fehler ist. Ich arbeite grundsätzlich über Klassen, in denen die Datenbankabfragen laufen. Alles ist über Try..Finally abgesichert, dass jedes object auch wieder freigegeben wird (kann höchstens sein, dass ich es mal an einer Stelle übersehen habe. Aber soweit ich das bisher gesichtet habe, sollte das nicht der Fall sein).

Die Connection halte in einer globalen Klasse, die beim Programmstart erzeugt wird und übergebe die halt immer an die Querys.
Hier mal ein Beispiel für eine Query-Klasse:
Delphi-Quellcode:
procedure TZeitenList.LoadFromDB(Con: TUniConnection; Betriebguid: TGUID);
var
  q: TUniQuery;
  z: TZeiten;
begin
  q:=TUniQuery.Create(nil);
  try
    self.Clear;
    q.Connection:=Con;
    q.SQL.Text:='Select * from zeiten where betriebguid=:betriebguid order by ID';
    q.Params.ParamValues['betriebguid']:=Betriebguid.ToString;
    q.Active:=True;
    while not q.Eof do
    begin
      z:=TZeiten.Create;
      z.ID:=q.FieldByName('ID').AsInteger;
      z.guid:=StringToGUID(q.FieldByName('guid').AsString);
      z.betriebguid:=StringToGUID(q.FieldByName('betriebguid').AsString);
      z.zeitstr:=q.FieldByName('zeitstr').AsString;
      self.Add(z);
      q.Next;
    end;
    q.active:=False
  finally
    q.Free;
  end;
end;
Ungefähr so sehen alle Abfragen aus.
Und meine globale Klasse sieht so aus:
Delphi-Quellcode:
unit Data.tpdbaccess;

interface

uses Classes, System.SysUtils, Data.DB, DBAccess, Uni, Tools.globalTypes, MySQLUniProvider, Local.ConnectionList, Tools.globalConst,
  MemData;

type
  TOnConnect=procedure(Verindungsname: string) of object;
  TOnDisconnect=procedure of object;
  TOnSendDBUpdateMessage=procedure(Msg: string; Append: Boolean) of object;

  TDBAccess=class
  private
    class var FCon: TUniConnection;
    class var FConnectionList: TConnectionList;
    class var FVerbindungsname: string;
    class var FOnConnect: TOnConnect;
    class var FOnDisconnect: TOnDisconnect;
    class var FOnSendDBUpdatemessage: TOnSendDBUpdateMessage;
    class constructor Create; overload;
    class procedure SetConnectionList(const Value: TConnectionList); static;
    class procedure SetVerbindungsname(const Value: string); static;
    class procedure DoConnect(Verbindungsname: string);
    class procedure DoDisconnect;
    class procedure AfterConnect(Sender: TObject);
    class procedure BeforeDisconnect(Sender: TObject);
    class procedure ConnectionLost(Sender: TObject; Component: TComponent; ConnLoseCouse: TConnLostCause; RetryMode: TRetryMode);
  public
    constructor Create(ConnectionItem: TConnectionItem); overload;
    destructor Destroy; override;
    class function open: Boolean; overload;
    class function open(ConnectionItem: TConnectionItem): Boolean; overload;
    class procedure Close;
    class property ConnectionList: TConnectionList read FConnectionList write SetConnectionList;
    class property Verbindungsname: string read FVerbindungsname write SetVerbindungsname;
    class procedure DoSendDBUpdateMessage(Msg: string; Append: Boolean = False);
    class property OnConnect: TOnConnect read FOnConnect write FOnConnect;
    class property OnDisconnect: TOnDisconnect read FOnDisconnect write FOnDisconnect;
    class property OnSendDBUpdateMessage: TOnSendDBUpdateMessage read FOnSendDBUpdatemessage write FOnSendDBUpdatemessage;
    class procedure GetTables(Strings: TStrings);
    class function ExecuteStatement(Statement: string): Variant; overload;
    class function ExecuteStatement(Statement: string; Params: array of Variant): Variant; overload;
    class function GetNewGUID: TGUID;
    class property Con: TUniConnection read FCon;
  end;

const
  NullGUID='{00000000-0000-0000-0000-000000000000}';

implementation

{ TDBAccess }

class constructor TDBAccess.Create;
begin
  inherited;
  FCon:=TUniConnection.Create(nil);
  FCon.AfterConnect:=AfterConnect;
  FCon.BeforeDisconnect:=BeforeDisconnect;
// FCon.OnConnectionLost:=ConnectionLost;
  FConnectionList:=TConnectionList.Create(true);
end;

class procedure TDBAccess.AfterConnect(Sender: TObject);
begin
  DoConnect(FVerbindungsname);
end;

class procedure TDBAccess.BeforeDisconnect(Sender: TObject);
begin
  DoDisconnect;
end;

class procedure TDBAccess.Close;
begin
  FCon.Disconnect;
  FVerbindungsname:='';
end;

class procedure TDBAccess.ConnectionLost(Sender: TObject; Component: TComponent;
  ConnLoseCouse: TConnLostCause; RetryMode: TRetryMode);
begin
  RetryMode:=rmReconnect;
end;

constructor TDBAccess.Create(ConnectionItem: TConnectionItem);
begin
  self.Create;
  FCon.ProviderName:=TTPConnectionTypeStr[integer(ConnectionItem.ConnectionType)];
  FCon.Server:=ConnectionItem.Hostname;
  FCon.Port:=ConnectionItem.Port;
  FCon.Database:=ConnectionItem.Database;
  FCon.Username:=ConnectionItem.UserName;
  FCon.Password:=ConnectionItem.Password;
  FVerbindungsname:=ConnectionItem.ConnectionName;
  open;
end;

destructor TDBAccess.Destroy;
begin
  if FCon.Connected then FCon.Disconnect;
  FConnectionList.Free;
  FCon.Free;
  inherited;
end;

class procedure TDBAccess.DoConnect(Verbindungsname: string);
begin
  if Assigned(FOnConnect) then
    FOnConnect(Verbindungsname);
end;

class procedure TDBAccess.DoDisconnect;
begin
  if Assigned(FOnDisconnect) then
    FOnDisconnect;
end;

class procedure TDBAccess.DoSendDBUpdateMessage(Msg: string; Append: Boolean);
begin
  if Assigned(FOnSendDBUpdatemessage) then
    FOnSendDBUpdatemessage(Msg, Append);
end;

class function TDBAccess.ExecuteStatement(Statement: string;
  Params: array of Variant): Variant;
begin
  Result:=FCon.ExecSQL(Statement, Params);
end;

class function TDBAccess.ExecuteStatement(Statement: string): Variant;
begin
  Result:=FCon.ExecSQL(Statement);
end;

class function TDBAccess.GetNewGUID: TGUID;
begin
  createGUID(Result);
end;

class procedure TDBAccess.GetTables(Strings: TStrings);
begin
  FCon.GetTableNames(Strings);
end;

class function TDBAccess.open: Boolean;
begin
  try
    Try
      FCon.Connect;
    Except
      FVerbindungsname:='';
    End;
  finally
    Result:=FCon.Connected;
  end;
end;

class function TDBAccess.open(ConnectionItem: TConnectionItem): Boolean;
begin
  FCon.ProviderName:=TTPConnectionTypeStr[integer(ConnectionItem.ConnectionType)];
  FCon.Server:=ConnectionItem.Hostname;
  FCon.Port:=ConnectionItem.Port;
  FCon.Database:=ConnectionItem.Database;
  FCon.Username:=ConnectionItem.UserName;
  FCon.Password:=ConnectionItem.Password;
  FVerbindungsname:=ConnectionItem.ConnectionName;
  Result:=open;
end;

class procedure TDBAccess.SetConnectionList(const Value: TConnectionList);
begin
  FConnectionList := Value;
end;

class procedure TDBAccess.SetVerbindungsname(const Value: string);
begin
  FVerbindungsname := Value;
end;

end.
Diese wird im Create der Mainform erzeugt und in deren Destroy auch wieder freigegeben.
(Die Threads erzeugen sich natürlich eine eigene TUniConnection).

Bisher hat das auch recht gut funktioniert. Leider habe ich nicht von Anfang an mit ReportMemoryLeaksOnShutDown gearbeitet. Die größe des Projekt macht es jetzt natürlich umso schwieriger.
Angehängte Grafiken
Dateityp: png ReportMemoryLeaksOnShutDown.png (205,1 KB, 17x aufgerufen)
Gruß Hobbycoder
Alle sagten: "Das geht nicht.". Dann kam einer, der wusste das nicht, und hat's einfach gemacht.
  Mit Zitat antworten Zitat
HolgerX

Registriert seit: 10. Apr 2006
Ort: Leverkusen
989 Beiträge
 
Delphi 6 Professional
 
#2

AW: Systemressourcen erschöpft beim VirtualStringTree.AfterCellPaint

  Alt 16. Nov 2018, 09:45
Hmm..


Die Connection halte in einer globalen Klasse, die beim Programmstart erzeugt wird und übergebe die halt immer an die Querys.
..
Und meine globale Klasse sieht so aus:
..
Diese wird im Create der Mainform erzeugt und in deren Destroy auch wieder freigegeben.
(Die Threads erzeugen sich natürlich eine eigene TUniConnection).

Wieso ist deine TDBAccess eigentlich komplett 'class xxx'.

Nimm doch mal hier das 'class' vor allen Funktionen weg und mach Dir ein 'echtes' Objecte im Create vom MainForm...

Vielleicht bringt dass das Speichermanagment etwas durcheinander und somit auch FMM..

(Nur so eine Idee von jemandem der 'class var .. ' nicht mag )
  Mit Zitat antworten Zitat
Antwort Antwort


Forumregeln

Es ist dir nicht erlaubt, neue Themen zu verfassen.
Es ist dir nicht erlaubt, auf Beiträge zu antworten.
Es ist dir nicht erlaubt, Anhänge hochzuladen.
Es ist dir nicht erlaubt, deine Beiträge zu bearbeiten.

BB-Code ist an.
Smileys sind an.
[IMG] Code ist an.
HTML-Code ist aus.
Trackbacks are an
Pingbacks are an
Refbacks are aus

Gehe zu:

Impressum · AGB · Datenschutz · Nach oben
Alle Zeitangaben in WEZ +1. Es ist jetzt 12:27 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