I've found a strange memory leak in a project that I've been newly assigned to.
On terminating, the program displays the following FastMM4 error message.
The project uses BusinessSkinForm
TbsaSpeedButtonSubClass
is from the third party BusinessSkinForm
library, however the speed buttons on that form appear to be regular VCL form controls.
When I add another speed button to the form,
I now have 25 instances of TbsaSpeedButtonSubClass
that leak instead of 24.
Which leads me to think that the leak is due to the TSpeedButton
.
However, this seems strange to me since I would expect form components to be automatically freed by the form upon destruction.
Perhaps BusinessSkinForm
does something unusual to the form which results in a leak...
I'm not sure how to get rid of this leak
EDIT
Thanks to KenWhite I have a memory leak report from FastMM4
EDIT
As shown in the stack trace, the problem can be traced to a TMUSICMainForm.SkinForm_OnCreate(SkinForm: TForm);
The problem seems to be related to a BSA: TbsaSkinAdapter
If I comment out the line BSA.ChangeSkinData;
the leak is no longer present.
EDIT
Here is the important part of the stack trace
--------------------------------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 ~ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
EDIT
I've managed to create a minimum working example of the memory leak.
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.
I've found that even if I comment out seemingly innocent looking lines like
BSF.BorderIcons:=[biMinimize,biMaximize];
or BSA.AdapterType := bsaUseClasses;
then the memory leak disappears.
Note my delphi form in this example contains 1 TSpeedButton
EDIT
I should also add that I'm using windows 7 (64bit), 6GB ram, delphi 5, Business Skin version 4.70
EDIT
the bsaadapter
unit in BusinessSkinForm contains a function
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;
It seems that speed buttons do not get freed because they are instances of TGraphicControl
if not (Control is TGraphicControl) then SC.Free;
While a regular TButton
will get freed
{$DEFINE FullDebugMode}
to mydpr
file. No output file was produced. – savFastMMOptions.inc
file to set the appropriate defines? Did you build your project (not compile)? – Ken White[]
toward the right side indicate line numbers. You can see exactly where the class in question is used, which indicates thatTSpeedButton
has been affected by the skin library code (looks like it's inbsadapter.pas
, from a quick glance). Use the source to figure out where the leak is occurring. FastMM is telling you exactly where to look. – Ken White