Delphi-PRAXiS
Seite 1 von 2  1 2      

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Algorithmen, Datenstrukturen und Klassendesign (https://www.delphipraxis.net/78-algorithmen-datenstrukturen-und-klassendesign/)
-   -   Clientdataset Speicherfreigabe (https://www.delphipraxis.net/210718-clientdataset-speicherfreigabe.html)

lxo 31. Mai 2022 14:38

Clientdataset Speicherfreigabe
 
Hallo zusammen,

wieso wird beim TClientdataset der Speicher nicht freigegeben wenn man Datensätze löscht?
Muss ich da noch irgendetwas auslösen? Beim TFDMemTable wird der Speicher direkt nach dem löschen wieder freigegeben.

Delphi-Quellcode:
unit Unit121;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Data.DB, Datasnap.DBClient, Vcl.StdCtrls,
  FireDAC.Stan.Intf, FireDAC.Stan.Option, FireDAC.Stan.Param,
  FireDAC.Stan.Error, FireDAC.DatS, FireDAC.Phys.Intf, FireDAC.DApt.Intf,
  FireDAC.Comp.DataSet, FireDAC.Comp.Client;

type
  TForm121 = class(TForm)
    gb_clientdataset: TGroupBox;
    b_clientdataset_add: TButton;
    b_clientdataset_delete: TButton;
    l_clientdataset: TLabel;
    gb_fdmemtable: TGroupBox;
    l_fdmemtable: TLabel;
    b_fdmemtable_add: TButton;
    b_fdmemtable_delete: TButton;
    procedure b_clientdataset_addClick(Sender: TObject);
    procedure b_clientdataset_deleteClick(Sender: TObject);
    procedure b_fdmemtable_addClick(Sender: TObject);
    procedure b_fdmemtable_deleteClick(Sender: TObject);
    procedure FormCreate(Sender: TObject);
  private
    { Private-Deklarationen }
  public
    { Public-Deklarationen }
    lClientDataSet: TClientDataSet;
    lFDMemTable: TFDMemTable;
    procedure Add( xDataset: TDataSet;
                   xLabel: TLabel);
    procedure Delete( xDataset: TDataSet;
                      xLabel: TLabel);
  end;

var
  Form121: TForm121;

implementation

{$R *.dfm}

procedure TForm121.Add(xDataset: TDataSet; xLabel: TLabel);
begin
  for var i := 1 to 9999 do
  begin
    xDataset.Append;
    xDataset.FieldByName('TEST1').AsString := Random( MaxInt).ToString;
    xDataset.FieldByName('TEST2').AsString := Random( MaxInt).ToString;
    xDataset.FieldByName('TEST3').AsString := Random( MaxInt).ToString;
    xDataset.Post;
  end;
  xLabel.Caption := Format( 'Recordcount: %d', [ xDataset.RecordCount]);
end;

procedure TForm121.b_clientdataset_addClick(Sender: TObject);
begin
  Add( lClientDataSet,
       l_clientdataset);
end;

procedure TForm121.b_clientdataset_deleteClick(Sender: TObject);
begin
  Delete( lClientDataSet,
          l_clientdataset);
end;

procedure TForm121.b_fdmemtable_addClick(Sender: TObject);
begin
  Add( lFDMemTable,
       l_fdmemtable);
end;

procedure TForm121.b_fdmemtable_deleteClick(Sender: TObject);
begin
  Delete( lFDMemTable,
          l_fdmemtable);
end;

procedure TForm121.Delete(xDataset: TDataSet; xLabel: TLabel);
begin
  while ( xDataset.RecordCount > 0) do
  begin
    xDataset.Delete;
  end;
  xLabel.Caption := Format( 'Recordcount: %d', [ xDataset.RecordCount]);
end;

procedure TForm121.FormCreate(Sender: TObject);
begin
  Randomize;
  lClientDataSet := TClientDataSet.Create( Self);
  lClientDataSet.FieldDefs.Add( 'TEST1', ftString, 500);
  lClientDataSet.FieldDefs.Add( 'TEST2', ftString, 500);
  lClientDataSet.FieldDefs.Add( 'TEST3', ftString, 500);
  lClientDataSet.CreateDataSet;

  lFDMemTable := TFDMemTable.Create( Self);
  lFDMemTable.FieldDefs.Add( 'TEST1', ftString, 500);
  lFDMemTable.FieldDefs.Add( 'TEST2', ftString, 500);
  lFDMemTable.FieldDefs.Add( 'TEST3', ftString, 500);
  lFDMemTable.CreateDataSet;
end;

end.

Uwe Raabe 31. Mai 2022 14:50

AW: Clientdataset Speicherfreigabe
 
Hast du es mal mit MergeChangeLog probiert?

lxo 31. Mai 2022 15:02

AW: Clientdataset Speicherfreigabe
 
Zitat:

Zitat von Uwe Raabe (Beitrag 1506564)
Hast du es mal mit MergeChangeLog probiert?

Wenn ich nach dem Insert und nach dem Delete MergeChangeLog ausführe habe ich den Unterschied, dass beim erneuten Insert von 9999 Datensätzen er vermutlich den selben Speicher verwendet. Trotzdem wird der Speicher nicht freigegeben.

Wenn ich nur an einer stelle Insert oder Delete MergeChangeLog ausführe wächst die Auslastung bei jedem neuen Insert.

LogChanges hilft auch nicht.

himitsu 31. Mai 2022 15:10

AW: Clientdataset Speicherfreigabe
 
Welcher Speicher wächst?

Ich tippe mal auf eine Speicherfragmentierung im FastMM.
Was sagt denn GetMemoryManagerState?



Hat ein billiges ClientDataSet überhaupt ein ChangeLog?

lxo 31. Mai 2022 15:55

AW: Clientdataset Speicherfreigabe
 
Zitat:

Zitat von himitsu (Beitrag 1506568)
Welcher Speicher wächst?

Ich tippe mal auf eine Speicherfragmentierung im FastMM.
Was sagt denn GetMemoryManagerState?



Hat ein billiges ClientDataSet überhaupt ein ChangeLog?

Die Arbeitsspeicherauslastung (Arbeitsspeicher aktiver privater Arbeitssatz), wird beim entfernen von Datensätzen nicht kleiner.



Clientdataset
  1. Programm Start - 2648K
  2. Add - 18900K
  3. Delete - 18800K
  4. Add - 33476K
  5. Delete - 33476K
  6. Add - 48792K

FDMemTable
  1. Programm Start - 2596K
  2. Add - 19648K
  3. Delete - 6852K
  4. Add - 19712K
  5. Delete - 6912K
  6. Add - 19712K

Was sagt denn GetMemoryManagerState?
- Da steht immer das selbe hättest du da ein Beispiel wie ich das richtig anwende bzw. was möchtest du da genau sehen?



Beim dxMemTable von DevExpress wird der Speicher auch freigegeben.
Wenn ich bei einer TList oder TObjectList Einträge entferne werden die auch direkt freigegeben.
Ich hab das Problem nur bei TClientDataSets

lxo 31. Mai 2022 17:22

AW: Clientdataset Speicherfreigabe
 
Liste der Anhänge anzeigen (Anzahl: 1)
Hier nochmal das komplette Testprojekt.

himitsu 31. Mai 2022 17:23

AW: Clientdataset Speicherfreigabe
 
Die scheinen den Speicher direkt bei Windows zu reservieren.

Hmmm, im FastMM steigt nichts an.
Aber im Code (Datasnap.DBClient) finde ich nur ein AllocMem, was eigentlich im Delphi-MemoryManager (FastMM) landen sollte. :gruebel:

Ich komm nach genügend Durchläufen (Add+Delete) sogar in einen OutOfMemory.


Delphi-Quellcode:
procedure TForm8.FormCreate(Sender: TObject);
var
  CDS: TClientDataSet;
  Mem: UInt64;
procedure ShowState;
  var
    GStatus: TMemoryStatusEx;
    MMState: TMemoryManagerState;
  begin
    //Memo1.Lines.Add('  Records ' + CDS.RecordCount.ToString);

    GStatus.dwLength := SizeOf(GStatus);
    GlobalMemoryStatusEx(GStatus);

    GetMemoryManagerState(MMState);
    var FastMM: Int64 := 0;
    for var i := 0 to High(MMState.SmallBlockTypeStates) do
      Inc(FastMM, MMState.SmallBlockTypeStates[i].UseableBlockSize * MMState.SmallBlockTypeStates[i].AllocatedBlockCount);
    Inc(FastMM, MMState.TotalAllocatedMediumBlockSize);
    Inc(FastMM, MMState.TotalAllocatedLargeBlockSize);

    Memo1.Lines.Add(Format('  Memory %d%% %.2nm %s%.2nm / %.2nm', [
      GStatus.dwMemoryLoad, Int64(GStatus.ullTotalVirtual - GStatus.ullAvailVirtual) / 1048576,
      IfThen(Mem < GStatus.ullAvailVirtual, '', '+'), Int64(Mem - GStatus.ullAvailVirtual) / 1048576,
      FastMM / 1048576
    ]));
    Mem := GStatus.ullAvailVirtual;
  end;
begin
  Mem := 0;
  CDS := TClientDataSet.Create(Self);
  CDS.FieldDefs.Add('TEST1', ftString, 500);
  CDS.FieldDefs.Add('TEST2', ftString, 500);
  CDS.FieldDefs.Add('TEST3', ftString, 500);
  CDS.CreateDataSet;

  for var L := 1 to 99 do
    try
      Memo1.Lines.Add(L.ToString);
      //Memo1.Lines.Add('Add');
      for var i := 1 to 9999 do begin
        CDS.Append;
        CDS.FieldByName('TEST1').AsString := Random(MaxInt).ToString;
        CDS.FieldByName('TEST2').AsString := Random(MaxInt).ToString;
        CDS.FieldByName('TEST3').AsString := Random(MaxInt).ToString;
        CDS.Post;
      end;
      ShowState;

      //Memo1.Lines.Add('Delete');
      while CDS.RecordCount > 0 do
        CDS.Delete;
      ShowState;
    except
      on E: Exception do begin
        Memo1.Lines.Add(L.ToString + ' : ' + E.Message);
        ShowState;
        Break;
      end;
    end;
end;

Uwe Raabe 1. Jun 2022 09:56

AW: Clientdataset Speicherfreigabe
 
Vielleicht ist es nicht jedem bewusst, aber die DB-Engine des TClientDataset liegt in der Midas.dll bzw. ihrem eingelinkten binären Zwilling. Damit arbeitet es schon etwas anders als die reine Delphi-Implementierung von FireDAC.

TigerLilly 1. Jun 2022 15:48

AW: Clientdataset Speicherfreigabe
 
Die ClientDataSets sind ja eigentlich für n-tier Anwendungen gemacht, hängen also an einem Provider, der die das Änderungslog (DELTA) weiterreicht. Ein DELETE löscht also nicht wirklich, sondern hebt alles auf, was notwendig ist, um die Änderung weiterreichen zu können UND um das Löschen auch rückgängig machen zu können. Siehe "CancelUpdates".
Es überrascht mich also nicht, dass Speicher alloziert bleibt.

Jedes Feld gibt es im CDS 3x: newValue/Value/oldValue - da kommt im Code unten schon (unrealistischerweise) was zusammen: 99 x 9999 x 3 x 3 x 500

Mit dem Zerstören des CDS wird dann auch der Speicher freigegeben.

Frickler 1. Jun 2022 16:32

AW: Clientdataset Speicherfreigabe
 
Einfach mal nach "CreateDataSet" die Property "LogChanges" auf false setzen.


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