Jump to content
santiago

Is it possible to know when a TObject is instanciated/freed?

Recommended Posts

Hi there,

 

I need to be notified whenever any TObject is created/freed. (for debugging purposes).

I have not found anything so far.
Maybe someone knows of a way?

 

Two ideas I have are:

 

  1. Look deeper into FastMM4. Maybe FastMM provides some mechanism to be notified of such events.
  2. See if it is possible to hijack the TObject Create and Destroy functions. Replace them with a function of mine, and then call the original function afterwards. My function would be acting like some sort of tranparent proxy.
    As of now I have no idea how to accomplish this, but this is something I want to investigate.

 

Would be very grateful is someone has more insight into this.

Share this post


Link to post

You can do this for any class hierarchy you wrote yourself, but doing it for any class starting with TObject would be difficult without patching routines of the run-time library.  The full version of FastMM4 has more options for tracing memory leaks that the one used in the Delphi RTL, I think, but I have never had to investigate this myself. Even if you manage to do it somehow you will drown in data; any even moderately complex program will create and delete tens of thousands of objects during a program run, and most of the collected data will be totally useless for figuring out where in your code the problem point is located.

 

Are you trying to track a particular problem? If so, give us more detail, perhaps there is a more focused way to handle that.

Share this post


Link to post

@PeterBelow

Our program is leaking memory. If I perform a certain calculation, there are some left-over objects that should no longer be referenced, but are alive.
What I need is the ability to create snapshots while the program is running. That way I can compare two snapshots and figure out which objects are still alive in the later snapshot and of course be able to have the callstack to how these objects were created.

 

In the past I have used the AQTime allocation profiler for investigating such issues. The problem is that AQtime has become practically unusable for us. Performing a particular calculation while AQTime is attached takes forever. Our app is a Delphi App, but makes heavy use of .NET and unmanaged (c++) dll's. So far I have not been able to figure out what the reason for the massive AQTime slowdown is.

 

So I extended an internal tool that we use for debugging with the functionality I described above. In order to have an object be taken into account, I need to derive from a special base class.
This works well, is what I need. At a any point in time I have insight into live objects (incl. callstack, and can also RTTI-Inspect them).

 

I am just looking for a way of avoiding having to change the base classes of the objects that I wish to inspect, and automatically take all objects into account.

Unfortunately our base classes inherit directly from TInterfacedObject. There are also many, many classes that inherit directly from TInterfacedObject. That is why I would need to change many classes to take them all into account.


Maybe I just need to bite the bullet and build in a common base class. That would would make it really easy...

Share this post


Link to post
uses
  Spring.VirtualClass;

procedure NotifyFree(const Self: TObject);
var
  freeInstance: TFreeInstance;
begin
  freeInstance := GetClassData(Self.ClassParent).FreeInstance;
  Writeln('Object of class ', Self.ClassName, ' destroyed');
  freeInstance(Self);
end;

var
  vc: TVirtualClasses;
  o: TObject;
begin
  vc := TVirtualClasses.Create;
  try
    o := TObject.Create;
    try
      vc.Proxify(o);
      GetClassData(o.ClassType).FreeInstance := NotifyFree;
    finally
      o.Free;
    end;
  finally
    vc.Free;
  end;
end.

 

Edited by Stefan Glienke
Fixed NotifyFree to properly call "inherited" FreeInstance
  • Like 2
  • Thanks 3

Share this post


Link to post
10 hours ago, santiago said:

@PeterBelow

Our program is leaking memory. If I perform a certain calculation, there are some left-over objects that should no longer be referenced, but are alive

....

Unfortunately our base classes inherit directly from TInterfacedObject. There are also many, many classes that inherit directly from TInterfacedObject. That is why I would need to change many classes to take them all into account.


Maybe I just need to bite the bullet and build in a common base class. That would would make it really easy...

Looks like a reference counting problem. Do you have classes where an object referenced by another also has to keep a backreference to the object that references it? That is the most common cause of such problems if the backreference is not declared as weak, but you probably know that already. Another problem scenario is passing interface references across a module boundary where it may be necessary to call _addref and _release manually, that's easy to screw up.

Share this post


Link to post

When I want to check this type of thing I create a debugging profiles specifically for CodeSite tracing and I install the trace calls with IFDEFs (in the constructors and destructors) so they are not in the main debugging code or release. You can then see a call hierarchy for objects. Obviously you would need the full version of CodeSite for this.

  • Like 1

Share this post


Link to post
13 hours ago, Pawel Piotrowski said:

You can use the mm4 memory leacks tracing  ability.

or you can use madExcept (http://www.madshi.net/madExceptDescription.htm).
There is an option "Report ressource leacks" that will print a very useful report when the application exits.
You can read more about it here: http://help.madshi.net/madExceptSettings1.htm

 

@Pawel Piotrowski

We do use madExcept. I need the leak report between snapshots though. While the application is still runnning.

Share this post


Link to post
12 hours ago, Der schöne Günther said:

We can compile our app with or without runtime packages. Unfortunately AQ Time crashes whenever I load our exe and it is NOT using runtime packages.

I suspect that the problem is related to some third paty component, as I had used AQTime after the Win10 creators update. It was slow, but at least I could work with it.

Share this post


Link to post
10 hours ago, PeterBelow said:

Looks like a reference counting problem. Do you have classes where an object referenced by another also has to keep a backreference to the object that references it? That is the most common cause of such problems if the backreference is not declared as weak, but you probably know that already. Another problem scenario is passing interface references across a module boundary where it may be necessary to call _addref and _release manually, that's easy to screw up.

Yes we do use weak references to avoid circular references. We only pass delphi interfaces to .NET and we rely on the .NET COM interop functionallity to manage the delphi object lifetime.

Share this post


Link to post

This is the best solution I have found so far. It might be of use to someone else.

 

  1. I call  GetMemoryManager to retrieve the FastMM memory manager and keep a reference to it.
  2. I then register my own memory manager. I only provide new implementations of GetMem and FreeMem. For all other calls I just forward the call to the FastMM memory manager.
  3. In my implementation of GetMem, I first delegate the call to the FastMM memory manager.
  4. I then call the FastMM function DetectClassInstance.
    {Returns the class for a memory block. Returns nil if it is not a valid class}
    function DetectClassInstance(APointer: Pointer): TClass;
    I was actually not expecting this to work, as I thought that calling this function at this stage would be way too early, and the call would always return nil.
    But to my pleasant surprise, it works. It returns the TClass for the just allocated memory block. 🙂
Edited by santiago
  • Like 1

Share this post


Link to post

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

×