Delphi 表单控件内存泄漏
Memory Leak on Delphi Form Controlls
我在新分配到的项目中发现了一个奇怪的内存泄漏。
终止时,程序显示以下 FastMM4 错误消息。
项目使用BusinessSkinForm
TbsaSpeedButtonSubClass
来自第三方 BusinessSkinForm
库,但是该窗体上的速度按钮似乎是常规的 VCL 窗体控件。
当我在表单中添加另一个速度按钮时,
我现在有 25 个 TbsaSpeedButtonSubClass
实例泄漏,而不是 24 个。
这让我认为泄漏是由于 TSpeedButton
。
然而,这对我来说似乎很奇怪,因为我希望表单组件在销毁时被表单自动释放。
也许 BusinessSkinForm
对表格做了一些不寻常的事情导致泄漏...
我不确定如何摆脱这个漏洞
编辑
感谢 KenWhite,我收到了来自 FastMM4 的内存泄漏报告
编辑
如堆栈跟踪所示,问题可以追溯到 TMUSICMainForm.SkinForm_OnCreate(SkinForm: TForm);
问题似乎与BSA: TbsaSkinAdapter
有关
如果我注释掉 BSA.ChangeSkinData;
行
泄漏不再存在。
编辑
这是堆栈跟踪的重要部分
--------------------------------2015/11/24 12:16:03-------------------------------- A memory block has been
leaked. The size is: 308
This block was allocated by thread 0x1258, and the stack trace (return
addresses) at the time was: 402AB6 [madZip][madZip][@GetMem] 4035F9
[madCrypt][madCrypt][TObject.NewInstance] 4039CA
[madCrypt][madCrypt][@ClassCreate] 67438A
[bsaadapter.pas][bsaadapter][TbsaSpeedButtonSubclass.Create][11537]
66137E [bsaadapter.pas][bsaadapter][TbsaHook.SetControl][2637] 403A1E
[madCrypt][madCrypt][@AfterConstruction] 665BFB
[bsaadapter.pas][bsaadapter][TbsaSkinManager.DoControlMessage][4898]
6615B7
[bsaadapter.pas][bsaadapter][TbsaHookCollection.AddControl][2760]
404ACB [madExcept][madExcept][@LStrSetLength] 662A0E
[bsaadapter.pas][bsaadapter][TbsaSkinManager.CollectSpeedButton][3544]
92C81D
[Sources\uMainForm.pas][uMainForm][TMUSICMainForm.SkinForm_OnCreate][4778]
The block is currently used for an object of class:
TbsaSpeedButtonSubclass
The allocation number is: 475863
Current memory dump of 256 bytes starting at pointer address 7E8A7670:
64 C9 65 00 00 00 00 00 00 00 00 00 00 00 00 00 68 F3 48 00 50 96 97
7E B8 5E 74 7E 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 B8 D1 74 7E 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 d É e . . . . . . . . . . . . . h ó H . P
– — ~ ¸ ^ t ~ . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . ¸ Ñ t ~ . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . .
编辑
我已经设法创建了内存泄漏的最小工作示例。
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
BusinessSkinForm, bsaadapter, Buttons
;
type
TForm1 = class(TForm)
SpeedButton1: TSpeedButton;
procedure FormCreate(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.DFM}
procedure TForm1.FormCreate(Sender: TObject);
var
BSF: TbsBusinessSkinForm;
BSA: TbsaSkinAdapter;
begin
BSF := TbsBusinessSkinForm.Create(Self);
BSF.BorderIcons:=[biMinimize,biMaximize];
BSA := TbsaSkinAdapter.Create(Self);
BSA.AdapterType := bsaUseClasses;
BSA.ChangeSkinData;
end;
end.
我发现即使我注释掉看似无辜的线条
BSF.BorderIcons:=[biMinimize,biMaximize];
或 BSA.AdapterType := bsaUseClasses;
那么内存泄漏就消失了。
注意我的 delphi 表单在这个例子中包含 1 TSpeedButton
编辑
我还应该补充一点,我正在使用 windows 7(64 位),6GB 内存,delphi 5,Business Skin 版本 4.70
编辑
BusinessSkinForm 中的 bsaadapter
单元包含一个函数
procedure TbsaSkinManager.DoUnhook(Control: TControl; Handle: HWnd);
var
i: integer;
SC: TbsaSubclass;
R: TRect;
begin
if FUnhooking then Exit;
if FUnhookedList = nil then
begin
FUnhooking := true;
Exit;
end;
FUnhooking := true;
try
for i := FHandleList.Count - 1 downto 0 do
begin
SC := TbsaSubclass(FHandleList[i]);
if (Handle <> 0) and (SC.Handle = Handle) then
begin
R := Rect(0, 0, 2000, 2000);
PostMessage(Handle, WM_NCPAINT, 0, 0);
InvalidateRect(Handle, @R, false);
FHandleList.Delete(i);
SC.Free;
end;
if (Control <> nil) and (SC.Control = Control) then
begin
FHandleList.Delete(i);
SC.FControl := nil;
if not (Control is TGraphicControl) then
SC.Free;
end;
end;
finally
FUnhooking := false;
end;
end;
似乎速度按钮没有被释放,因为它们是 TGraphicControl
的实例
if not (Control is TGraphicControl) then
SC.Free;
虽然常规 TButton
会被释放
从我在这里看到的情况来看,有很多方法可以从这里开始。
提交对 BusinessSkinForm 的修复以处理速度按钮
接受变通方法
即:BSA.AdapterType := bsaUseNames;
而不是 BSA.AdapterType := bsaUseClasses;
用常规 TButton
替换速度按钮。
我在新分配到的项目中发现了一个奇怪的内存泄漏。
终止时,程序显示以下 FastMM4 错误消息。
项目使用BusinessSkinForm
TbsaSpeedButtonSubClass
来自第三方 BusinessSkinForm
库,但是该窗体上的速度按钮似乎是常规的 VCL 窗体控件。
当我在表单中添加另一个速度按钮时,
我现在有 25 个 TbsaSpeedButtonSubClass
实例泄漏,而不是 24 个。
这让我认为泄漏是由于 TSpeedButton
。
然而,这对我来说似乎很奇怪,因为我希望表单组件在销毁时被表单自动释放。
也许 BusinessSkinForm
对表格做了一些不寻常的事情导致泄漏...
我不确定如何摆脱这个漏洞
编辑
感谢 KenWhite,我收到了来自 FastMM4 的内存泄漏报告
编辑
如堆栈跟踪所示,问题可以追溯到 TMUSICMainForm.SkinForm_OnCreate(SkinForm: TForm);
问题似乎与BSA: TbsaSkinAdapter
如果我注释掉 BSA.ChangeSkinData;
泄漏不再存在。
编辑
这是堆栈跟踪的重要部分
--------------------------------2015/11/24 12:16:03-------------------------------- A memory block has been leaked. The size is: 308
This block was allocated by thread 0x1258, and the stack trace (return addresses) at the time was: 402AB6 [madZip][madZip][@GetMem] 4035F9 [madCrypt][madCrypt][TObject.NewInstance] 4039CA [madCrypt][madCrypt][@ClassCreate] 67438A [bsaadapter.pas][bsaadapter][TbsaSpeedButtonSubclass.Create][11537] 66137E [bsaadapter.pas][bsaadapter][TbsaHook.SetControl][2637] 403A1E [madCrypt][madCrypt][@AfterConstruction] 665BFB [bsaadapter.pas][bsaadapter][TbsaSkinManager.DoControlMessage][4898] 6615B7 [bsaadapter.pas][bsaadapter][TbsaHookCollection.AddControl][2760] 404ACB [madExcept][madExcept][@LStrSetLength] 662A0E [bsaadapter.pas][bsaadapter][TbsaSkinManager.CollectSpeedButton][3544] 92C81D [Sources\uMainForm.pas][uMainForm][TMUSICMainForm.SkinForm_OnCreate][4778]
The block is currently used for an object of class: TbsaSpeedButtonSubclass
The allocation number is: 475863
Current memory dump of 256 bytes starting at pointer address 7E8A7670: 64 C9 65 00 00 00 00 00 00 00 00 00 00 00 00 00 68 F3 48 00 50 96 97 7E B8 5E 74 7E 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 B8 D1 74 7E 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 d É e . . . . . . . . . . . . . h ó H . P – — ~ ¸ ^ t ~ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ¸ Ñ t ~ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
编辑
我已经设法创建了内存泄漏的最小工作示例。
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
BusinessSkinForm, bsaadapter, Buttons
;
type
TForm1 = class(TForm)
SpeedButton1: TSpeedButton;
procedure FormCreate(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.DFM}
procedure TForm1.FormCreate(Sender: TObject);
var
BSF: TbsBusinessSkinForm;
BSA: TbsaSkinAdapter;
begin
BSF := TbsBusinessSkinForm.Create(Self);
BSF.BorderIcons:=[biMinimize,biMaximize];
BSA := TbsaSkinAdapter.Create(Self);
BSA.AdapterType := bsaUseClasses;
BSA.ChangeSkinData;
end;
end.
我发现即使我注释掉看似无辜的线条
BSF.BorderIcons:=[biMinimize,biMaximize];
或 BSA.AdapterType := bsaUseClasses;
那么内存泄漏就消失了。
注意我的 delphi 表单在这个例子中包含 1 TSpeedButton
编辑
我还应该补充一点,我正在使用 windows 7(64 位),6GB 内存,delphi 5,Business Skin 版本 4.70
编辑
BusinessSkinForm 中的 bsaadapter
单元包含一个函数
procedure TbsaSkinManager.DoUnhook(Control: TControl; Handle: HWnd);
var
i: integer;
SC: TbsaSubclass;
R: TRect;
begin
if FUnhooking then Exit;
if FUnhookedList = nil then
begin
FUnhooking := true;
Exit;
end;
FUnhooking := true;
try
for i := FHandleList.Count - 1 downto 0 do
begin
SC := TbsaSubclass(FHandleList[i]);
if (Handle <> 0) and (SC.Handle = Handle) then
begin
R := Rect(0, 0, 2000, 2000);
PostMessage(Handle, WM_NCPAINT, 0, 0);
InvalidateRect(Handle, @R, false);
FHandleList.Delete(i);
SC.Free;
end;
if (Control <> nil) and (SC.Control = Control) then
begin
FHandleList.Delete(i);
SC.FControl := nil;
if not (Control is TGraphicControl) then
SC.Free;
end;
end;
finally
FUnhooking := false;
end;
end;
似乎速度按钮没有被释放,因为它们是 TGraphicControl
if not (Control is TGraphicControl) then SC.Free;
虽然常规 TButton
会被释放
从我在这里看到的情况来看,有很多方法可以从这里开始。
提交对 BusinessSkinForm 的修复以处理速度按钮
接受变通方法 即:
BSA.AdapterType := bsaUseNames;
而不是BSA.AdapterType := bsaUseClasses;
用常规
TButton
替换速度按钮。