【发布时间】:2016-02-26 07:02:36
【问题描述】:
我在新分配到的一个项目中发现了一个奇怪的内存泄漏。
在终止时,程序会显示以下 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----- --------------------------- 一个内存块已经 泄露。大小为:308
这个块是由线程 0x1258 分配的,堆栈跟踪(返回 地址)当时是: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 [来源\uMainForm.pas][uMainForm][TMUSICMainForm.SkinForm_OnCreate][4778]
该块当前用于类对象: TbsaSpeedButton子类
分配号为:475863
从指针地址 7E8A7670 开始的 256 字节的当前内存转储: 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。磷 – — ~ ¸ ^ 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 的实例
如果不是(Control 是 TGraphicControl)那么 SC.免费;
虽然普通的TButton 将被释放
【问题讨论】:
-
您可以按照您发布的对话框中注意中的说明开始,这将引导您找到特定的代码。详情请见this answer。
-
我将
{$DEFINE FullDebugMode}添加到我的dpr文件中。没有生成输出文件。 -
您是否完成了完整构建,而不是编译?
-
您是否还编辑了
FastMMOptions.inc文件以设置适当的定义?您是否构建了您的项目(不是编译)? -
所以查看堆栈跟踪。
[]右侧的数字表示行号。您可以确切地看到所讨论的类的使用位置,这表明TSpeedButton已受到皮肤库代码的影响(乍一看,它似乎在bsadapter.pas中)。使用来源找出泄漏发生的位置。 FastMM 会准确地告诉您去哪里看。
标签: forms delphi memory-leaks vcl fastmm