调用表格并以正确的方式发布?内存使用情况
Call a Form and correct way to release? memory usage
我正在使用 Delphi 7.
我有两个单元,包含 Form1
和 Form2
。 secondary form在某些过程中会被多次调用,很担心内存占用
当我启动程序时,内存使用量约为 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;
里面 Unit2
:
procedure TForm2.FormClose(Sender: TObject; var Action: TCloseAction);
begin
Action := caFree;
end;
我是不是做错了什么?有更好的解决方案吗?
拜托,这不是一个简单的问题,因为这个程序会运行 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.
此程序正在读取指纹。
Unit1:
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.
第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.
我知道任务管理器是验证内存泄漏的最佳工具,但几个小时后,程序增长速度越来越快,并没有释放内存。
我让程序运行通宵了,内存在备份,但是用户使用的时间太长了。
不要在同一对象上同时使用 Action := caFree
和 Free()
。使用一个或另一个,而不是两个。对 Free()
的调用实际上会抵消 Action:=caFree
,使其变得多余。
但这不是问题的原因。
您显示的代码总体上没有问题,Form2
正确地从内存中释放。您没有考虑到的是 Delphi 的内存管理器根本不会将释放的内存释放回 OS,它会保留它并将其重新用于将来的分配。如果您使用任务管理器来跟踪内存使用情况,那不是诊断内存泄漏的好工具,因为它不知道 Delphi 如何缓存随时间释放的内存。
但是,话虽如此,您描述的增长听起来更像是内存碎片而不是内存泄漏。但很难确定,因为您没有显示 Form2 上的所有内容,也没有提供 Minimal, Complete, and Verifiable example 来重现问题。
无论如何,Delphi 7 的默认内存管理器通常效率不高。这就是 Borland 最终在 Delphi 2006 年改用 FastMM 的原因。您可以尝试用更新的内存管理器替换默认内存管理器,看看是否有帮助。
不,除了表单可能 "Freed" 两次的潜在错误外,假设您的 Form2 实现本身不正确,您似乎做的一切都是正确的引入内存泄漏。
我假设您正在使用 任务管理器 监控 "memory use"。如果是这样,这是非常具有误导性的。
您的应用程序正在使用 Delphi 堆管理器管理其内存。此堆管理器将根据需要分配内存(从 OS 请求它)但是当它被释放时它不会立即 returned 到系统(OS),而是简单地标记为 no使用时间更长。然后,当应用程序将来需要内存时,堆管理器可以 "recycle" 这个未使用的内存,而不是必须返回 OS 请求更多内存(这是一个相对 "expensive"操作)。
但是,堆管理器确定是否可以回收未使用的内存以满足新请求的方式可能意味着可能无法回收的内存,例如碎片化的结果。
假设您的应用程序分配了 500 字节的内存,然后再分配 100 字节,然后再分配 500 字节:
[1] [used] 500 bytes
[2] [used] 100 bytes
[3] [used] 500 bytes
想象一下,这两个 500 字节的块被释放,可以重新使用它们。
[1] [free] 500 bytes
[2] [used] 100 bytes
[3] [free] 500 bytes
您可能认为请求 1000 字节(或什至 600、700、800 字节等)将能够使用此 "recyclable" 内存。
但是 1000 字节的请求需要一个连续的块,并且该 100 字节的块仍在使用中,这两个 500 字节的块只能用于每个(最多)500 字节的请求。因此,必须通过分配 new 1000 字节块来满足 1000 字节的请求:
[1] [free] 500 bytes
[2] [used] 100 bytes
[3] [free] 500 bytes
[4] [used] 1000 bytes
您的应用程序仍然只有 "using" 1100 字节,但为此,已经从 OS.
分配了 2100 字节
所有这一切的最终结果是内存 "use" 似乎在任务管理器中增长,而实际上真正发生的是您的应用程序只是 "holding on" 分配的内存它不是实际使用时间更长,以防将来可能需要它。
如果 OS 达到需要内存恢复的程度,那么所有进程都将被要求放弃此类内存,您的应用程序也不例外。
您可以通过最小化和恢复您的应用程序来模拟这种情况。除非您的应用程序真正使用了当前分配的所有内存,否则您应该会看到内存使用量下降。这种下降可能很小,也可能很大,具体取决于您的应用程序的内存使用情况。
当 (Delphi) 应用程序最小化时,它将 return some/as 系统占用大量内存,因为如果用户已最小化它,那么它现在是一个"backgrond" 近期不太可能需要内存的进程。
您可以在您的应用程序 OnIdle 事件中使用一些代码来触发此行为,但这样做几乎毫无意义。它可能会给 印象 任务管理器 中减少的内存使用,但可能会降低应用程序的性能并且实际上不会减少内存使用.
我该怎么办?
Delphi 运行时一直支持用替代实现替换应用程序堆管理器的能力。
一个流行的标准(自 Delphi 2006 年起被采纳为新标准)是 FastMM。这实现了避免或减少内存碎片的策略,并提供了其他性能和调试改进(例如,它可以配置为报告内存泄漏和错误使用对已销毁对象的引用)。
FastMM 是开源的,甚至可以在 Delphi 7 应用程序中使用。您需要做的就是下载源代码并添加 FastMM 作为项目中使用的第一个单元 dpr 文件。
使用 FastMM。请参阅 article 并添加 FastMM。
如果可以,请使用完全启用的 FastMM 诊断和 运行 创建单独的构建配置。 e.在合并到 master git 分支之前。我使用这种策略并大大提高了我的代码质量,没有任何缺点(FastMM 诊断对编译器来说有点繁重,但我 运行 它们不经常使用,所以没关系)。
我正在使用 Delphi 7.
我有两个单元,包含 Form1
和 Form2
。 secondary form在某些过程中会被多次调用,很担心内存占用
当我启动程序时,内存使用量约为 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;
里面 Unit2
:
procedure TForm2.FormClose(Sender: TObject; var Action: TCloseAction);
begin
Action := caFree;
end;
我是不是做错了什么?有更好的解决方案吗?
拜托,这不是一个简单的问题,因为这个程序会运行 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.
此程序正在读取指纹。
Unit1:
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.
第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.
我知道任务管理器是验证内存泄漏的最佳工具,但几个小时后,程序增长速度越来越快,并没有释放内存。
我让程序运行通宵了,内存在备份,但是用户使用的时间太长了。
不要在同一对象上同时使用 Action := caFree
和 Free()
。使用一个或另一个,而不是两个。对 Free()
的调用实际上会抵消 Action:=caFree
,使其变得多余。
但这不是问题的原因。
您显示的代码总体上没有问题,Form2
正确地从内存中释放。您没有考虑到的是 Delphi 的内存管理器根本不会将释放的内存释放回 OS,它会保留它并将其重新用于将来的分配。如果您使用任务管理器来跟踪内存使用情况,那不是诊断内存泄漏的好工具,因为它不知道 Delphi 如何缓存随时间释放的内存。
但是,话虽如此,您描述的增长听起来更像是内存碎片而不是内存泄漏。但很难确定,因为您没有显示 Form2 上的所有内容,也没有提供 Minimal, Complete, and Verifiable example 来重现问题。
无论如何,Delphi 7 的默认内存管理器通常效率不高。这就是 Borland 最终在 Delphi 2006 年改用 FastMM 的原因。您可以尝试用更新的内存管理器替换默认内存管理器,看看是否有帮助。
不,除了表单可能 "Freed" 两次的潜在错误外,假设您的 Form2 实现本身不正确,您似乎做的一切都是正确的引入内存泄漏。
我假设您正在使用 任务管理器 监控 "memory use"。如果是这样,这是非常具有误导性的。
您的应用程序正在使用 Delphi 堆管理器管理其内存。此堆管理器将根据需要分配内存(从 OS 请求它)但是当它被释放时它不会立即 returned 到系统(OS),而是简单地标记为 no使用时间更长。然后,当应用程序将来需要内存时,堆管理器可以 "recycle" 这个未使用的内存,而不是必须返回 OS 请求更多内存(这是一个相对 "expensive"操作)。
但是,堆管理器确定是否可以回收未使用的内存以满足新请求的方式可能意味着可能无法回收的内存,例如碎片化的结果。
假设您的应用程序分配了 500 字节的内存,然后再分配 100 字节,然后再分配 500 字节:
[1] [used] 500 bytes
[2] [used] 100 bytes
[3] [used] 500 bytes
想象一下,这两个 500 字节的块被释放,可以重新使用它们。
[1] [free] 500 bytes
[2] [used] 100 bytes
[3] [free] 500 bytes
您可能认为请求 1000 字节(或什至 600、700、800 字节等)将能够使用此 "recyclable" 内存。
但是 1000 字节的请求需要一个连续的块,并且该 100 字节的块仍在使用中,这两个 500 字节的块只能用于每个(最多)500 字节的请求。因此,必须通过分配 new 1000 字节块来满足 1000 字节的请求:
[1] [free] 500 bytes
[2] [used] 100 bytes
[3] [free] 500 bytes
[4] [used] 1000 bytes
您的应用程序仍然只有 "using" 1100 字节,但为此,已经从 OS.
分配了 2100 字节所有这一切的最终结果是内存 "use" 似乎在任务管理器中增长,而实际上真正发生的是您的应用程序只是 "holding on" 分配的内存它不是实际使用时间更长,以防将来可能需要它。
如果 OS 达到需要内存恢复的程度,那么所有进程都将被要求放弃此类内存,您的应用程序也不例外。
您可以通过最小化和恢复您的应用程序来模拟这种情况。除非您的应用程序真正使用了当前分配的所有内存,否则您应该会看到内存使用量下降。这种下降可能很小,也可能很大,具体取决于您的应用程序的内存使用情况。
当 (Delphi) 应用程序最小化时,它将 return some/as 系统占用大量内存,因为如果用户已最小化它,那么它现在是一个"backgrond" 近期不太可能需要内存的进程。
您可以在您的应用程序 OnIdle 事件中使用一些代码来触发此行为,但这样做几乎毫无意义。它可能会给 印象 任务管理器 中减少的内存使用,但可能会降低应用程序的性能并且实际上不会减少内存使用.
我该怎么办?
Delphi 运行时一直支持用替代实现替换应用程序堆管理器的能力。
一个流行的标准(自 Delphi 2006 年起被采纳为新标准)是 FastMM。这实现了避免或减少内存碎片的策略,并提供了其他性能和调试改进(例如,它可以配置为报告内存泄漏和错误使用对已销毁对象的引用)。
FastMM 是开源的,甚至可以在 Delphi 7 应用程序中使用。您需要做的就是下载源代码并添加 FastMM 作为项目中使用的第一个单元 dpr 文件。
使用 FastMM。请参阅 article 并添加 FastMM。
如果可以,请使用完全启用的 FastMM 诊断和 运行 创建单独的构建配置。 e.在合并到 master git 分支之前。我使用这种策略并大大提高了我的代码质量,没有任何缺点(FastMM 诊断对编译器来说有点繁重,但我 运行 它们不经常使用,所以没关系)。