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

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

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

项目使用BusinessSkinForm

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

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

我现在有 25 个 TbsaSpeedButtonSubClass 实例泄漏,而不是 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
相关资源
最近更新 更多