致电表格并更正发布方式?内存使用情况

Wel*_*nha 0 delphi delphi-7

我正在使用Delphi 7.

我有两个包含Form1和的单位Form2.在某些过程中会多次调用辅助表单,我非常担心内存使用情况.

当我启动该程序时,内存使用量约为2.1 MB.当Form2被调用时,内存增大到2.9 MB.在此过程之后,我关闭Form2并再次调用它来模拟常规使用,内存增长到3.1 MB,再次调用,内存增长到3.4 MB,3.6 MB,3.8 MB等.

内存使用是主要问题.

Form1这样叫Form2:

uses
  Unit2;

...

private
  { Private declarations }
  FChild : TForm2;
...      

FChild := TForm2.Create(nil);
try
  FChild.ShowModal;
finally
  FChild.Free;
end;
Run Code Online (Sandbox Code Playgroud)

内部Unit2:

procedure TForm2.FormClose(Sender: TObject; var Action: TCloseAction);
begin
  Action := caFree;
end;
Run Code Online (Sandbox Code Playgroud)

难道我做错了什么?有更好的解决方案吗?

请问,这不是一个简单的问题,因为这个程序将运行24小时,第二个表单将被多次调用.这意味着有时这个程序会冻结计算机.

我在项目中包含了FASTMM4:

program Project1;

uses
  FastMM4,
  Forms,
  Unit1 in 'Unit1.pas' {Form1},
  Unit2 in 'Unit2.pas' {Form2};

{$R *.res}

begin
  FullDebugModeScanMemoryPoolBeforeEveryOperation := True;
  SuppressMessageBoxes:=False;
  Application.Initialize;
  Application.CreateForm(TForm1, Form1);
  Application.Run;
end.
Run Code Online (Sandbox Code Playgroud)

这个程序正在读取指纹.

单元1:

unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, ExtCtrls, Unit2;

type
  TForm1 = class(TForm)
    btnForm2: TButton;
    btnReselease: TButton;
    procedure btnForm2Click(Sender: TObject);
    procedure btnReseleaseClick(Sender: TObject);
  private
    { Private declarations }
    FChild : TForm2;
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.btnForm2Click(Sender: TObject);

begin
  FChild := TForm2.Create(nil);
  try
    FChild.ShowModal;
  finally
    FChild.Free;
  end;
end;

procedure TForm1.btnReseleaseClick(Sender: TObject);
begin
  if FChild <> nil then
  begin
    FreeAndNil(FChild);
  end;
end;

end.
Run Code Online (Sandbox Code Playgroud)

单元2:

unit Unit2;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, Buttons, pngimage, ExtCtrls, Grids,  XMLIntf, XMLDoc,
  ComCtrls, CPort;

type
  TForm2 = class(TForm)
    btnSyncFP: TBitBtn;
    btnExitFP: TBitBtn;
    btnDeleteFP: TBitBtn;
    btnCaptureFP: TBitBtn;
    ComImage: TImage;
    FPGrid: TStringGrid;
    prgBar: TProgressBar;
    lblbar: TLabel;
    ComPortA: TComPort;
    ComDataPacket1: TComDataPacket;
    procedure LoadUsers2;
    procedure FormCreate(Sender: TObject);
    procedure ComPortAAfterOpen(Sender: TObject);
    procedure ComPortAAfterClose(Sender: TObject);
    procedure btnExitFPClick(Sender: TObject);
    procedure FormClose(Sender: TObject; var Action: TCloseAction);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
//  Form2: TForm2;
  L1, L2: TStringList;

implementation
{$R *.dfm}

procedure TForm2.LoadUsers2;
var
  XML : IXMLDOCUMENT;
  CurNode, CNode : IXMLNODE;
  i : Integer;
begin
  XML := NewXMLDocument;
  XML.LoadFromFile('usuario.xml');
  XML.Active := True;
  CurNode := XML.DocumentElement.ChildNodes[0]; // users
  FPGrid.RowCount := CurNode.ChildNodes.Count+1;
  prgBar.Min := 0;
  prgBar.Max := CurNode.ChildNodes.Count-1;
  lblBar.Caption := 'Carregando usuários...';
  for i := 0 to CurNode.ChildNodes.Count-1 do
  begin
    CNode := CurNode.ChildNodes[i]; // groups
    with CNode do
    begin
      FPGrid.Cells[2,i+1] := Attributes['group'];
      FPGrid.Cells[1,i+1] := Attributes['id'];
      FPGrid.Cells[0,i+1] := Attributes['name'];
      FPGrid.Cells[3,i+1] := Attributes['fingerID'];
      FPGrid.Cells[4,i+1] := Attributes['level'];
      FPGrid.Cells[5,i+1] := Attributes['status'];
    end;
    if FPGrid.Cells[3,i+1]<>'' then L1.Add(FPGrid.Cells[3,i+1]);
    prgBar.Position := i;
  end;
  XML := nil;
end;

procedure TForm2.FormCreate(Sender: TObject);
begin
  LoadUsers2;
  with FPGrid do
  begin
    Font.Name := 'Tahoma';
    Font.Size := 12;
    ColCount := 4;
    Cells[0,0] := 'Name';  Cells[1,0] := 'ID';
    Cells[2,0] := 'Group'; Cells[3,0] := 'Read ID';
    Cells[4,0] := 'Level'; Cells[5,0] := 'Status';
    ScrollBars := ssVertical;
    Options := Options + [goRowSelect];
  end;
  ComPortA.Open;
end;

procedure TForm2.ComPortAAfterOpen(Sender: TObject);
begin
  ComImage.Picture.LoadFromFile('conn_on.png');
end;

procedure TForm2.ComPortAAfterClose(Sender: TObject);
begin
  ComImage.Picture.LoadFromFile('conn_off.png');
end;

procedure TForm2.btnExitFPClick(Sender: TObject);
begin
  ComPortA.Close;
  Close;
end;

procedure TForm2.FormClose(Sender: TObject; var Action: TCloseAction);
begin
  Action := caFree;
end;

end.
Run Code Online (Sandbox Code Playgroud)

我知道任务管理器是最好的验证泄漏内存,但几个小时后,程序增长更快,而不是重新释放内存.

我让程序整夜运行并且内存正在备份,但是用户使用时间很长.

Del*_*ics 5

不,除了表单可能被"释放"两次的潜在错误之外,您似乎正在正确地执行所有操作,假设您的Form2实现本身不会引入内存泄漏.

我假设您正在使用任务管理器监视"内存使用" .如果是这样,这是非常误导的.

您的应用程序正在使用Delphi堆管理器管理其内存.此堆管理器将根据需要分配内存(从OS请求它),但是当它被释放时,它不会立即返回到系统(OS),而只是标记为不再使用.然后,当将来应用程序需要内存时,堆管理器可以"回收"这个未使用的内存,而不必返回操作系统以请求更多内存(这是一个相对"昂贵"的操作).

但是,堆管理器确定是否可以回收未使用的内存以满足新请求的方式可能意味着可能无法回收的内存可能不是,例如由于碎片的结果.

想象一下,您的应用程序分配了500个字节的内存,然后再分配100个字节,然后再分配500个字节:

[1] [used] 500 bytes
[2] [used] 100 bytes
[3] [used] 500 bytes
Run Code Online (Sandbox Code Playgroud)

想象一下,释放了两个500字节块,使它们可以重复使用.

[1] [free] 500 bytes
[2] [used] 100 bytes
[3] [free] 500 bytes
Run Code Online (Sandbox Code Playgroud)

您可能认为1000字节(甚至600,700,800字节等)的请求将能够使用此"可循环"内存.

但是,对1000字节的请求需要一个连续的块,并且仍然使用该100字节块,这两个500字节块只能用于每个(最多)500字节的请求.因此,必须通过分配新的 1000字节块来满足1000字节的请求:

[1] [free] 500 bytes
[2] [used] 100 bytes
[3] [free] 500 bytes
[4] [used] 1000 bytes
Run Code Online (Sandbox Code Playgroud)

您的应用程序仍然只"使用"1100个字节,但为此,已从OS分配了2100个字节.

所有这一切的最终结果是内存"使用"在任务管理器中看起来会增长,实际上真正发生的事情是你的应用程序只是"坚持"它不再实际使用的已分配内存,以防万一可能在将来需要它.

如果操作系统达到了需要内存的程度,那么将要求所有进程放弃这样的内存,并且您的应用程序也不例外.

您可以通过最小化和恢复应用程序来模拟这一点.除非您的应用程序真正使用当前分配的所有内存,否则您应该看到内存使用量下降.此丢弃可能很小,也可能很大,具体取决于应用程序的内存使用情况.

当(Delphi)应用程序被最小化时,它将向系统返回一些/尽可能多的内存,基于如果用户已将其最小化,那么它现在是一个"背景"过程,其不太可能需要对记住在不久的将来.

您可以使用应用程序OnIdle事件中的某些代码触发此行为,但这样做大多没有意义.它可能会给任务管理器中内存使用减少的印象,但可能会降低应用程序的性能,并且实际上不会减少内存使用量.

我能做些什么?

Delphi运行时始终支持使用替代实现替换应用程序堆管理器的能力.

一个受欢迎的(自Delphi 2006以来被采用为新标准)是FastMM.这实现了避免或减少内存碎片的策略,并提供了其他性能和调试改进(例如,它可以配置为报告内存泄漏和错误使用的对已销毁对象的引用).

FastMM是开源的,甚至可以在Delphi 7应用程序中使用.您需要做的就是下载源代码并添加FastMM作为项目dpr文件中使用的第一个单元.