【问题标题】:Memory Leak on Delphi Form ControllsDelphi 表单控件上的内存泄漏
【发布时间】:2016-02-26 07:02:36
【问题描述】:

我在新分配到的一个项目中发现了一个奇怪的内存泄漏。

在终止时,程序会显示以下 FastMM4 错误消息。

项目使用BusinessSkinForm

TbsaSpeedButtonSubClass 来自第三方BusinessSkinForm 库,但是该表单上的速度按钮似乎是常规的 VCL 表单控件。

当我在表单中添加另一个速度按钮时,

我现在有 25TbsaSpeedButtonSubClass 实例泄漏,而不是 24 个。

这让我认为泄漏是由于TSpeedButton。 然而,这对我来说似乎很奇怪,因为我希望表单组件在销毁时自动被表单释放。

也许BusinessSkinForm 对表单做了一些不寻常的事情导致泄漏......

我不知道如何摆脱这种泄漏

编辑

感谢 KenWhite,我收到了来自 FastMM4 的内存泄漏报告

Here it is on Pastebin

编辑

如堆栈跟踪所示,问题可以追溯到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


【解决方案1】:

从我在这里看到的情况来看,我可以从这里开始有很多方法。

  1. 向 BusinessSkinForm 提交修复以处理速度按钮

  2. 使用解决方法 即:BSA.AdapterType := bsaUseNames; 而不是BSA.AdapterType := bsaUseClasses;

  3. 将速度按钮替换为常规的TButton

【讨论】:

  • 我会让这个打开一段时间,以防其他人有其他建议。感谢所有帮助:)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-04-18
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多