Sign in Join

Live chat for Free Pascal, Lazarus, Delphi and every Pascal programmer.

Mailing lists / fpc-pascal / 2017-09

Where and Why is there a memory leak?

20 messages · 7 people · –

Reply by email Subscribe to this list Your subscription

Read-only archive. Use j and k to move between messages.

Graeme Geldenhuys

Hi,

Playing with this small sample application to answer another question in this mailing list, I noticed the sample application has a memory leak. For the life of me I can't see why or how to resolve it.

I tested with FPC 2.6.4, 3.0.2 and 3.0.4-rc1 under 64-bit FreeBSD.

=======================[ project1.pas ]============================
program project1;
{$mode objfpc}{$H+}
{$interfaces COM}
type
   IHook = interface
     ['{4BCAEDD8-92D8-11E7-88D3-C86000E37EB0}']
     procedure DoIt;
   end;
type
   THook = class(TInterfacedObject, IHook)
   private
     procedure DoIt;
   end;
   procedure THook.DoIt;
   begin
     writeln(ClassName + ' did it');
   end;
type
   TBaseClass = class(TInterfacedObject, IHook)
   private
     FHook: IHook;
     property Hook: IHook read FHook implements IHook;
   public
     constructor Create;
     destructor Destroy; override;
   end;
   constructor TBaseClass.Create;
   begin
     FHook := THook.Create;  // FPC 2.6.4 reports a memory leak here
   end;
   destructor TBaseClass.Destroy;
   begin
     // nothing to do here
   end;
var
   base: IHook;
begin
   base := TBaseClass.Create;
   base.DoIt;
   base := nil; // just to see if it helped with the memory leak - it
doesn't
end.
==============================[ end ]==============================

When I run the program, the output is as follows:

[t1]$ ./project1
THook did it
Heap dump by heaptrc unit
4 memory blocks allocated : 115/120
2 memory blocks freed : 51/56
2 unfreed memory blocks : 64
True heap size : 1114112 (32 used in System startup) True free heap : 1113696
Should be : 1113760
Call trace for block $000000080072F180 size 32
$0000000000400379 line 35 of project1.lpr
Call trace for block $000000080072F0C0 size 32

Personally I always use CORBA style interfaces, never reference counted COM style interfaces. So my programs normally don't have this issue, and I use interfaces a lot.

Regards,
Graeme

fpGUI Toolkit - a cross-platform GUI toolkit using Free Pascal http://fpgui.sourceforge.net/ My public PGP key: http://tinyurl.com/graeme-pgp
Michael Van Canneyt
to Graeme Geldenhuys
Graeme Geldenhuys wrote 50 lines
Hi, Playing with this small sample application to answer another question in this mailing list, I noticed the sample application has a memory leak. For the life of me I can't see why or how to resolve it. I tested with FPC 2.6.4, 3.0.2 and 3.0.4-rc1 under 64-bit FreeBSD. =======================[ project1.pas ]============================ program project1; {$mode objfpc}{$H+} {$interfaces COM} type IHook = interface ['{4BCAEDD8-92D8-11E7-88D3-C86000E37EB0}'] procedure DoIt; end; type THook = class(TInterfacedObject, IHook) private procedure DoIt; end; procedure THook.DoIt; begin writeln(ClassName + ' did it'); end; type TBaseClass = class(TInterfacedObject, IHook) private FHook: IHook; property Hook: IHook read FHook implements IHook; public constructor Create; destructor Destroy; override; end; constructor TBaseClass.Create; begin FHook := THook.Create; // FPC 2.6.4 reports a memory leak here end; destructor TBaseClass.Destroy; begin // nothing to do here end;

You must free FHook here, because you are keeping a reference to the object, not the interface. And you must call inherited.
You must always call inherited in the destructor.

Michael.

Graeme Geldenhuys
to Michael Van Canneyt
Michael Van Canneyt wrote 21 lines
> type > TBaseClass = class(TInterfacedObject, IHook) > private > FHook: IHook; > property Hook: IHook read FHook implements IHook; > public > constructor Create; > destructor Destroy; override; > end; > > constructor TBaseClass.Create; > begin > FHook := THook.Create; // FPC 2.6.4 reports a memory leak here > end; > > destructor TBaseClass.Destroy; > begin > // nothing to do here > end; You must free FHook here, because you are keeping a reference to the object, not the interface.

I changed the destructor to the code shown below. Just so you know, I tried this before I posted the message, and it didn't make any difference. FHook is a IHook interface reference, not a object reference. I can't change it either, otherwise FPC gives me a compiler error on the property ... implements...; line.

   destructor TBaseClass.Destroy;
   begin
     inherited;
     FHook := nil;
   end;

And the program output gives:

[t1]$ ./project1
THook did it
Heap dump by heaptrc unit
4 memory blocks allocated : 115/120
2 memory blocks freed : 51/56
2 unfreed memory blocks : 64
True heap size : 1114112 (32 used in System startup) True free heap : 1113696
Should be : 1113760
Call trace for block $000000080072F180 size 32
$0000000000400379 line 35 of project1.lpr
Call trace for block $000000080072F0C0 size 32

Still 2 memory leaks. :-(

Regards,
Graeme

fpGUI Toolkit - a cross-platform GUI toolkit using Free Pascal http://fpgui.sourceforge.net/ My public PGP key: http://tinyurl.com/graeme-pgp
Ryan Joseph
to Graeme Geldenhuys

Didn’t you say:

FHook := THook.Create;

so you need a FHook.Free call? The class is THook = class(TInterfacedObject, IHook) right?

On Sep 6, 2017, at 4:24 PM, Graeme Geldenhuys wrote: I changed the destructor to the code shown below. Just so you know, I tried this before I posted the message, and it didn't make any difference. FHook is a IHook interface reference, not a object reference. I can't change it either, otherwise FPC gives me a compiler error on the property ... implements...; line.

Regards,
Ryan Joseph

Sven Barth
to Graeme Geldenhuys: Playing with this small sample application to answer another question in this mailing…

Am 06.09.2017 10:31 schrieb "Graeme Geldenhuys" <

Show quoted text 17 lines
: type IHook = interface ['{4BCAEDD8-92D8-11E7-88D3-C86000E37EB0}'] procedure DoIt; end; type THook = class(TInterfacedObject, IHook) private procedure DoIt; end; procedure THook.DoIt; begin writeln(ClassName + ' did it'); end;

I think THook needs to derive from TAggregatedObject, cause that couples the reference counting to that of the controlling instance. See here:
http://docwiki.embarcadero.com/RADStudio/Tokyo/en/Implementing_Interfaces

Regards,
Sven

Graeme Geldenhuys
to Sven Barth
Sven Barth wrote
I think THook needs to derive from TAggregatedObject, cause that couples the reference counting to that of the controlling instance.

That seems to be heading in the right direction, but such a change on its own doesn't seem to solve the two memory leaks either. I'll test under Delphi XE which can report memory leaks to see what it does with that same code. So this might end up being a bug in FPC somewhere.

Either way, modifying the example to use a getter method AND TAggregatedObject, I managed to get rid of both memory leaks. Here is the working [memory leak free] code now:

=======================[ project1.pas ]=======================
program project1;
{$mode objfpc}{$H+}
{$interfaces COM}
type
   IHook = interface
     ['{4BCAEDD8-92D8-11E7-88D3-C86000E37EB0}']
     procedure DoIt;
   end;
type
   THook = class(TAggregatedObject, IHook)
   private
     procedure DoIt;
   end;
   procedure THook.DoIt;
   begin
     writeln(ClassName + ' did it');
   end;
type
   TBaseClass = class(TInterfacedObject, IHook)
   private
     FHookInstance: THook;
     function GetHook: IHook;
     property Hook: IHook read GetHook implements IHook;
   public
     destructor Destroy; override;
   end;
   function TBaseClass.GetHook: IHook;
   begin
     if FHookInstance = nil then
       FHookInstance := THook.Create(self);
     Result := FHookInstance;
   end;
   destructor TBaseClass.Destroy;
   begin
     FHookInstance.Free;
     inherited Destroy;
   end;
var
   base: IHook;
begin
   base := TBaseClass.Create;
   base.DoIt;
   base := nil; // just to see if it helped with the memory leak - it
doesn't
end.
========================[ end ]===============================

And the program output:

[t1]$ ./project1
THook did it
Heap dump by heaptrc unit
4 memory blocks allocated : 107/112
4 memory blocks freed : 107/112
0 unfreed memory blocks : 0
True heap size : 1114112 (32 used in System startup) True free heap : 1114080

I can't remember ever [while using Delphi] being forced to implement a getter method in the "property ... implements..." line, but it seems that is the only way it remove memory leaks under FPC. Weird. :-/

Regards,
Graeme

fpGUI Toolkit - a cross-platform GUI toolkit using Free Pascal http://fpgui.sourceforge.net/ My public PGP key: http://tinyurl.com/graeme-pgp
Ryan Joseph
to Graeme Geldenhuys
On Sep 6, 2017, at 4:55 PM, Graeme Geldenhuys wrote: Either way, modifying the example to use a getter method AND TAggregatedObject, I managed to get rid of both memory leaks. Here is the working [memory leak free] code now:

Does the original code work if you just add the Free() call? I would expect it to leak without that.

Regards,
Ryan Joseph

Graeme Geldenhuys
to Graeme Geldenhuys: That seems to be heading in the right direction, but such a change on its own doesn't…
Graeme Geldenhuys wrote 7 lines
On 2017-09-06 10:41, Sven Barth via fpc-pascal wrote: > I think THook needs to derive from TAggregatedObject, cause that couples > the reference counting to that of the controlling instance. That seems to be heading in the right direction, but such a change on its own doesn't seem to solve the two memory leaks either. I'll test under Delphi XE which can report memory leaks to see what it does

Testing with Delphi XE - simply by changing THook to descend from TAggregatedObject. Under Delphi it also still reports a memory leak.

Either way, modifying the example to use a getter method AND TAggregatedObject, I managed to get rid of both memory leaks. Here is the working [memory leak free] code now:

Under Delphi XE, that was the only way to get rid of the memory leaks too.

So it seems for delegation and using "implements" you are forced to use a getter method and object variable (not interface reference variable) - under both FPC and Delphi. At least they are consistent. ;-)

ps:
For those that didn't know.... Since Delphi 2006, add

       ReportMemoryLeaksOnShutdown := True;
   in your *.dpr file to enable memory leak detection.

Regards,
Graeme

fpGUI Toolkit - a cross-platform GUI toolkit using Free Pascal http://fpgui.sourceforge.net/ My public PGP key: http://tinyurl.com/graeme-pgp
Marcos Douglas B. Santos
to Graeme Geldenhuys: That seems to be heading in the right direction, but such a change on its own doesn't…

On Wed, Sep 6, 2017 at 6:55 AM, Graeme Geldenhuys <

Show quoted text 6 lines
wrote: I can't remember ever [while using Delphi] being forced to implement a getter method in the "property ... implements..." line, but it seems that is the only way it remove memory leaks under FPC. Weird. :-/

You don't need this getter. You have resolved just by change FHookInstance as a class, not an Interface, plus using TAggregatedObject too.

I wrote an article about it
http://objectpascalprogramming.com/posts/interfaces-delegacao-problemas-solucoes/

(use Google Translator).

Best regards,
Marcos Douglas

Graeme Geldenhuys
to Marcos Douglas B. Santos
Marcos Douglas B. Santos wrote
You have resolved just by change FHookInstance as a class, not an Interface, plus using TAggregatedObject too.

Ah yes, that seems to work too. Many thanks for pointing that out.

So here is another implementation that works with NO memory leaks.

========================[ project1.pas ]============================
program project1;
{$mode objfpc}{$H+}
{$interfaces COM}
type
   IHook = interface
     ['{4BCAEDD8-92D8-11E7-88D3-C86000E37EB0}']
     procedure DoIt;
   end;
type
   THook = class(TAggregatedObject, IHook)
   private
     procedure DoIt;
   end;
   procedure THook.DoIt;
   begin
     writeln(ClassName + ' did it');
   end;
type
   TBaseClass = class(TInterfacedObject, IHook)
   private
     FHookInstance: THook;
     property Hook: THook read FHookInstance implements IHook;
   public
     constructor Create;
     destructor Destroy; override;
   end;
   constructor TBaseClass.Create;
   begin
     inherited Create;
     FHookInstance := THook.Create(self);
   end;
   destructor TBaseClass.Destroy;
   begin
     FHookInstance.Free;
     inherited Destroy;
   end;
var
   base: IHook;
begin
   base := TBaseClass.Create;
   base.DoIt;
end.
==============================[ end ]===============================

Yeah, just as I said.... Interfaces are an advanced feature of the Object Pascal language. Lots of traps!

Bottom line:

NEVER code without enabling memory leak detection!!! :)

Regards,
Graeme

fpGUI Toolkit - a cross-platform GUI toolkit using Free Pascal http://fpgui.sourceforge.net/ My public PGP key: http://tinyurl.com/graeme-pgp
Graeme Geldenhuys
to Ryan Joseph: Didn’t you say: FHook := THook.Create; so you need a FHook.Free call? The class is…
Ryan Joseph wrote
FHook := THook.Create; so you need a FHook.Free call? The class is THook = class(TInterfacedObject, IHook) right?

I couldn't call .Free because FHook was a interface reference type of type IHook, not THook.

Regards,
Graeme

fpGUI Toolkit - a cross-platform GUI toolkit using Free Pascal http://fpgui.sourceforge.net/ My public PGP key: http://tinyurl.com/graeme-pgp
Ryan Joseph
to Graeme Geldenhuys
On Sep 6, 2017, at 8:03 PM, Graeme Geldenhuys wrote: I couldn't call .Free because FHook was a interface reference type of type IHook, not THook.
But TInterfacedObject is a class isn’t it? Then you call FHook := THook.Create; so a I’d expect a Free(). What am I missing?
type
 THook = class(TInterfacedObject, IHook)
 private
   procedure DoIt;
 end;

Regards,
Ryan Joseph

Sven Barth
to Ryan Joseph
"Ryan Joseph" wrote 6 lines
> On Sep 6, 2017, at 8:03 PM, Graeme Geldenhuys < wrote: > > I couldn't call .Free because FHook was a interface reference type of

type IHook, not THook.

But TInterfacedObject is a class isn’t it? Then you call FHook :=

THook.Create; so a I’d expect a Free(). What am I missing?

Show quoted text 6 lines
type THook = class(TInterfacedObject, IHook) private procedure DoIt; end;

You're missing that FHook was declared as IHook, not THook.

Regards,
Sven

Tony Whyman
to Graeme Geldenhuys: Playing with this small sample application to answer another question in this mailing…

Is history repeating itself:

http://lists.freepascal.org/pipermail/fpc-pascal/2016-August/048579.html

Graeme Geldenhuys wrote 92 lines
Hi, Playing with this small sample application to answer another question in this mailing list, I noticed the sample application has a memory leak. For the life of me I can't see why or how to resolve it. I tested with FPC 2.6.4, 3.0.2 and 3.0.4-rc1 under 64-bit FreeBSD. =======================[ project1.pas ]============================ program project1; {$mode objfpc}{$H+} {$interfaces COM} type IHook = interface ['{4BCAEDD8-92D8-11E7-88D3-C86000E37EB0}'] procedure DoIt; end; type THook = class(TInterfacedObject, IHook) private procedure DoIt; end; procedure THook.DoIt; begin writeln(ClassName + ' did it'); end; type TBaseClass = class(TInterfacedObject, IHook) private FHook: IHook; property Hook: IHook read FHook implements IHook; public constructor Create; destructor Destroy; override; end; constructor TBaseClass.Create; begin FHook := THook.Create; // FPC 2.6.4 reports a memory leak here end; destructor TBaseClass.Destroy; begin // nothing to do here end; var base: IHook; begin base := TBaseClass.Create; base.DoIt; base := nil; // just to see if it helped with the memory leak - it doesn't end. ==============================[ end ]============================== When I run the program, the output is as follows: [t1]$ ./project1 THook did it Heap dump by heaptrc unit 4 memory blocks allocated : 115/120 2 memory blocks freed : 51/56 2 unfreed memory blocks : 64 True heap size : 1114112 (32 used in System startup) True free heap : 1113696 Should be : 1113760 Call trace for block $000000080072F180 size 32 $0000000000400379 line 35 of project1.lpr Call trace for block $000000080072F0C0 size 32 Personally I always use CORBA style interfaces, never reference counted COM style interfaces. So my programs normally don't have this issue, and I use interfaces a lot. Regards, Graeme
Graeme Geldenhuys
to Tony Whyman: Is history repeating itself: http://lists.freepascal.org/pipermail/fpc-pascal/2016-August/…
Tony Whyman wrote
Is history repeating itself:

Quite likely. It did all sound awfully familiar. :-)

I've now [finally] added this to my "Personal Programming Notes" archive (yes, I actually have such a file). So hopefully now I will not forget about the gotcha of memory leaks and reference counted objects.

Regards,
Graeme

fpGUI Toolkit - a cross-platform GUI toolkit using Free Pascal http://fpgui.sourceforge.net/ My public PGP key: http://tinyurl.com/graeme-pgp
Marcos Douglas B. Santos
to Ryan Joseph
Ryan Joseph wrote 8 lines
> On Sep 6, 2017, at 10:20 PM, Sven Barth via fpc-pascal wrote: > > You're missing that FHook was declared as IHook, not THook. > In terms of memory why does it matter how it was declared? The memory was allocated in Create() so the declaration is just relevant for the assignment of the new memory. Is FPC doing something magic behind the scenes?

Because COM interface variables are automatically released.

Sven Barth
"Ryan Joseph" wrote 9 lines
> On Sep 6, 2017, at 10:20 PM, Sven Barth via fpc-pascal < wrote: > > You're missing that FHook was declared as IHook, not THook. > In terms of memory why does it matter how it was declared? The memory was

allocated in Create() so the declaration is just relevant for the assignment of the new memory. Is FPC doing something magic behind the scenes?

a) since it's an interface it doesn't *have* a Free method b) COM interfaces are reference counted and the compiler (both Delphi and FPC) does automatic reference counting with them (that's the purpose of TInterfacedObject, it calls Free on itself once the reference count reaches 0)

Regards,
Sven

Michael Schnell
to Graeme Geldenhuys: Quite likely. It did all sound awfully familiar. :-) I've now [finally] added this to my…
Graeme Geldenhuys wrote
Quite likely. It did all sound awfully familiar. :-)

Is there even a thinkable solution ?

To me the offering of two incompatible kinds of interfaces (not regarding the external libraries but the language construct) seems rather odd, especially as the language construct is decently usable without attaching to an external library. In fact to me it's already off to create a language construct based on the dedicated makeup of some external would be better to have an independent and usable language construct that just offers means to optionally attach to certain types of libraries.

OTOH, another related construct already discusses multiple times (and seemingly offered by Embarcadero) is ref counted "normal" objects. But those are known to introduce certain pitfalls, as well.

-Michael