0
votes

Trying to fix memory leaks in my application using FastMM in full debug mode, some leaks are reported regarding instances of (descendants of) TForm. After checking and stepping in the code, I can say for sure that those forms are released and not freed. It looks as if FastMM is checking for leaks before the actual freeing scheduled by Release happens.

MyForm: TForm;
MyForm := TForm.Create(nil);
...
MyForm.Release; // FastMM reports MyForm as a leak

I tried freeing them instead, and those memory leaks are no longer being reported, but sometimes, an access violation occurs when freeing:

MyForm: TForm;
MyForm := TForm.Create(nil);
...
MyForm.Free; // // FastMM does not report MyForm as a leak, but sometimes an access violation is triggered

I tried releasing them, and call Application.ProcessMessages immediately after, as I understood could be done reading Zoë Peterson's answer in https://stackoverflow.com/a/917187/1465896. I must have understood wrong, because it crashes always immediately in Application.ProcessMessages:

MyForm: TForm;
MyForm := TForm.Create(nil);
...
MyForm.Release; 
Application.ProcessMessages; // always triggers an access violation 

I don't want to register those forms as expected memory leaks, because they contain more objects that clutter the memory leak log file, making it harder to find more important leaks.

So my question is, how to properly free a Delphi form so that FastMM won't report it as a leak?

As requested, here is an SSCnCE (Short, Self Contained, Correct (not Compilable as is because I don't know how to post the project), Example):

File FormRelease.dpr:

program FormRelease;

uses
  FastMM4 in 'FastMM4.pas',
  Vcl.Forms,
  MainForm_fm in 'MainForm_fm.pas' {MainForm},
  MyForm_fm in 'MyForm_fm.pas' {MyForm};

{$R *.res}

begin
  Application.Initialize;
  Application.MainFormOnTaskbar := True;
  Application.CreateForm(TMainForm, MainForm);
  Application.Run;
end.

File MainForm_fm.pas:

unit MainForm_fm;

interface

uses
  Vcl.Forms,
  MyForm_fm;

type
  TMainForm = class(TForm)
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);

  private
    FMyForm: TMyForm;
  end;

var
  MainForm: TMainForm;

implementation

{$R *.dfm}

procedure TMainForm.FormCreate(Sender: TObject);
begin
  FMyForm := TMyForm.Create(nil);
end;

procedure TMainForm.FormDestroy(Sender: TObject);
begin
  FMyForm.Release;
end;

end.

File MyForm_fm.pas:

unit MyForm_fm;

interface

uses
  Vcl.Forms;

type
  TMyForm = class(TForm)
  end;

implementation

{$R *.dfm}

end.

Excerpt from file FormRelease_MemoryManager_EventLog.txt, after starting the program and stopping it with alt-F4:

This application has leaked memory. The small block leaks are (excluding expected leaks registered by pointer):

13 - 20 bytes: TList x 1, Unknown x 1
21 - 36 bytes: TPen x 1, TMargins x 1, TPadding x 1, TIconImage x 1, TBrush x 2, TTouchManager x 1, TSizeConstraints x 1, UnicodeString x 1, Unknown x 3
37 - 52 bytes: TGlassFrame x 1, TFont x 2
53 - 68 bytes: TIcon x 1
69 - 84 bytes: TControlScrollBar x 2
101 - 116 bytes: TControlCanvas x 1
149 - 164 bytes: Unknown x 1
917 - 1012 bytes: TMyForm x 1

Why is there TMyForm x 1 left if the CM_RELEASE posted with FMyForm.Release has been handled?

1
I think the answer you linked does not promote calling Application.ProcessMessages yourself. It states that when Release is used, the resulting message will be handled there. Can you break down the code you use to create and destroy your forms? That would be Important for any answerer.nil
Calling Application.ProcessMessages directly after Release should be basically the same as calling Free. No postponing, what Release is for. Are you doing that in an event handler of the Form itself? That would explain the access violations.nil
@Nil I am not doing it from an event handler, but from a regular destructor, and even from a finalization section (the application I have to fix has a somewhat peculiar architecture).Papaya
@TomBrunberg I was so sure it was a problem specific to the application I am working on that I did not consider trying to create an example. How wrong I was! I added a really simple example that shows that a call to Release does not free the form.Papaya
Release is wrong here. ProcessMessages is almost always wrong here. Call Free to destroy the object. Or make it owner by the form.David Heffernan

1 Answers

1
votes

It sounds like two separate things.

First, you are explicitly creating your form, so you should explicitly free it. You shouldn't need to call Release except for special cases like a form freeing itself from within one of its own event handlers.

MyForm: TForm;
...
MyForm := TForm.Create(nil);
try
  // Only reference MyForm in this block
finally
  MyForm.Free;
end;

Second, calling Free isn't causing the problem. I think it's revealing it. If I had to guess, the access violation is happening because MyForm or one of its components is being referenced outside of this block. Use the debugger to find this reference and fix it.