如何正确释放 Delphi 表单
How to properly free a Delphi form
尝试在完全调试模式下使用 FastMM 修复我的应用程序中的内存泄漏,报告了一些关于 TForm(的后代)实例的泄漏。在检查并单步执行代码后,我可以肯定地说这些表单是 released 而不是 freed。看起来好像 FastMM 在 Release 计划的实际释放发生之前检查泄漏。
MyForm: TForm;
MyForm := TForm.Create(nil);
...
MyForm.Release; // FastMM reports MyForm as a leak
我尝试 释放 它们,并且不再报告这些内存泄漏,但有时,释放时会发生访问冲突:
MyForm: TForm;
MyForm := TForm.Create(nil);
...
MyForm.Free; // // FastMM does not report MyForm as a leak, but sometimes an access violation is triggered
我试过 释放 它们,然后立即调用 Application.ProcessMessages,据我所知,可以阅读 Zoë Peterson 在 中的回答。我一定理解错了,因为它总是在 Application.ProcessMessages:
中立即崩溃
MyForm: TForm;
MyForm := TForm.Create(nil);
...
MyForm.Release;
Application.ProcessMessages; // always triggers an access violation
我不想将这些表单注册为预期的内存泄漏,因为它们包含更多的对象,这些对象使内存泄漏日志文件变得混乱,使得更难找到更重要的泄漏。
所以我的问题是,如何正确地释放一个 Delphi 表单,这样 FastMM 就不会将其报告为泄漏?
根据要求,这是一个 SSCnCE(简短、自包含、正确(不可按原样编译,因为我不知道如何 post 项目),示例):
文件FormRelease.dpr:
program FormRelease;
uses
FastMM4 in 'FastMM4.pas',
Vcl.Forms,
MainForm_fm in 'MainForm_fm.pas' {MainForm},
MyForm_fm in 'MyForm_fm.pas' {MyForm};
{$R *.res}
begin
Application.Initialize;
Application.MainFormOnTaskbar := True;
Application.CreateForm(TMainForm, MainForm);
Application.Run;
end.
文件MainForm_fm.pas:
unit MainForm_fm;
interface
uses
Vcl.Forms,
MyForm_fm;
type
TMainForm = class(TForm)
procedure FormCreate(Sender: TObject);
procedure FormDestroy(Sender: TObject);
private
FMyForm: TMyForm;
end;
var
MainForm: TMainForm;
implementation
{$R *.dfm}
procedure TMainForm.FormCreate(Sender: TObject);
begin
FMyForm := TMyForm.Create(nil);
end;
procedure TMainForm.FormDestroy(Sender: TObject);
begin
FMyForm.Release;
end;
end.
文件MyForm_fm.pas:
unit MyForm_fm;
interface
uses
Vcl.Forms;
type
TMyForm = class(TForm)
end;
implementation
{$R *.dfm}
end.
摘自文件 FormRelease_MemoryManager_EventLog.txt,启动程序并使用 alt-F4 停止程序后:
This application has leaked memory. The small block leaks are (excluding expected leaks registered by pointer):
13 - 20 bytes: TList x 1, Unknown x 1
21 - 36 bytes: TPen x 1, TMargins x 1, TPadding x 1, TIconImage x 1, TBrush x 2, TTouchManager x 1, TSizeConstraints x 1, UnicodeString x 1, Unknown x 3
37 - 52 bytes: TGlassFrame x 1, TFont x 2
53 - 68 bytes: TIcon x 1
69 - 84 bytes: TControlScrollBar x 2
101 - 116 bytes: TControlCanvas x 1
149 - 164 bytes: Unknown x 1
917 - 1012 bytes: TMyForm x 1
如果 CM_RELEASE
post 和 FMyForm.Release
处理完了,为什么还剩下 TMyForm x 1
?
听起来像是两个不同的东西。
首先,您明确地创建了表单,因此您应该明确地释放它。您不需要调用 Release,除非是特殊情况,例如表单从其自己的事件处理程序之一中释放自身。
MyForm: TForm;
...
MyForm := TForm.Create(nil);
try
// Only reference MyForm in this block
finally
MyForm.Free;
end;
其次,调用 Free 不会导致问题。我认为它正在揭示它。如果我不得不猜测,访问冲突的发生是因为在该块之外引用了 MyForm 或其组件之一。使用调试器找到这个引用并修复它。
尝试在完全调试模式下使用 FastMM 修复我的应用程序中的内存泄漏,报告了一些关于 TForm(的后代)实例的泄漏。在检查并单步执行代码后,我可以肯定地说这些表单是 released 而不是 freed。看起来好像 FastMM 在 Release 计划的实际释放发生之前检查泄漏。
MyForm: TForm;
MyForm := TForm.Create(nil);
...
MyForm.Release; // FastMM reports MyForm as a leak
我尝试 释放 它们,并且不再报告这些内存泄漏,但有时,释放时会发生访问冲突:
MyForm: TForm;
MyForm := TForm.Create(nil);
...
MyForm.Free; // // FastMM does not report MyForm as a leak, but sometimes an access violation is triggered
我试过 释放 它们,然后立即调用 Application.ProcessMessages,据我所知,可以阅读 Zoë Peterson 在 中的回答。我一定理解错了,因为它总是在 Application.ProcessMessages:
中立即崩溃MyForm: TForm;
MyForm := TForm.Create(nil);
...
MyForm.Release;
Application.ProcessMessages; // always triggers an access violation
我不想将这些表单注册为预期的内存泄漏,因为它们包含更多的对象,这些对象使内存泄漏日志文件变得混乱,使得更难找到更重要的泄漏。
所以我的问题是,如何正确地释放一个 Delphi 表单,这样 FastMM 就不会将其报告为泄漏?
根据要求,这是一个 SSCnCE(简短、自包含、正确(不可按原样编译,因为我不知道如何 post 项目),示例):
文件FormRelease.dpr:
program FormRelease;
uses
FastMM4 in 'FastMM4.pas',
Vcl.Forms,
MainForm_fm in 'MainForm_fm.pas' {MainForm},
MyForm_fm in 'MyForm_fm.pas' {MyForm};
{$R *.res}
begin
Application.Initialize;
Application.MainFormOnTaskbar := True;
Application.CreateForm(TMainForm, MainForm);
Application.Run;
end.
文件MainForm_fm.pas:
unit MainForm_fm;
interface
uses
Vcl.Forms,
MyForm_fm;
type
TMainForm = class(TForm)
procedure FormCreate(Sender: TObject);
procedure FormDestroy(Sender: TObject);
private
FMyForm: TMyForm;
end;
var
MainForm: TMainForm;
implementation
{$R *.dfm}
procedure TMainForm.FormCreate(Sender: TObject);
begin
FMyForm := TMyForm.Create(nil);
end;
procedure TMainForm.FormDestroy(Sender: TObject);
begin
FMyForm.Release;
end;
end.
文件MyForm_fm.pas:
unit MyForm_fm;
interface
uses
Vcl.Forms;
type
TMyForm = class(TForm)
end;
implementation
{$R *.dfm}
end.
摘自文件 FormRelease_MemoryManager_EventLog.txt,启动程序并使用 alt-F4 停止程序后:
This application has leaked memory. The small block leaks are (excluding expected leaks registered by pointer):
13 - 20 bytes: TList x 1, Unknown x 1
21 - 36 bytes: TPen x 1, TMargins x 1, TPadding x 1, TIconImage x 1, TBrush x 2, TTouchManager x 1, TSizeConstraints x 1, UnicodeString x 1, Unknown x 3
37 - 52 bytes: TGlassFrame x 1, TFont x 2
53 - 68 bytes: TIcon x 1
69 - 84 bytes: TControlScrollBar x 2
101 - 116 bytes: TControlCanvas x 1
149 - 164 bytes: Unknown x 1
917 - 1012 bytes: TMyForm x 1
如果 CM_RELEASE
post 和 FMyForm.Release
处理完了,为什么还剩下 TMyForm x 1
?
听起来像是两个不同的东西。
首先,您明确地创建了表单,因此您应该明确地释放它。您不需要调用 Release,除非是特殊情况,例如表单从其自己的事件处理程序之一中释放自身。
MyForm: TForm;
...
MyForm := TForm.Create(nil);
try
// Only reference MyForm in this block
finally
MyForm.Free;
end;
其次,调用 Free 不会导致问题。我认为它正在揭示它。如果我不得不猜测,访问冲突的发生是因为在该块之外引用了 MyForm 或其组件之一。使用调试器找到这个引用并修复它。