A GC API that would be convenient

Normal

0

21

false

false

false

FR

X-NONE

X-NONE

Share/Bookmark

I take a chance to expose
a wish I have for quite some time. I would like the following method in the
System.GC class
:

 

public static
T[] GetAliveInstancesOf<T>() where T : class

 

By alive I mean all
instances reachable from the GC roots. I am not a GC specialist but I am pretty
sure that this method wouldn’t be that hard to develop. Btw, SOS
provides
this feature already.

 

With such method one would be
able to check at any time that all instances of a type satisfy some conditions.
More specifically I would like to check periodically that some code doesn’t
have memory leak by verifying some properties on the objects. For example, in the
NDepend code base we
use some kind of composite patterns. With such API, it would be easy to check
that all alive leaf instances are part of a valid graph (by checking recursively all
parents of all objects).

 

Such GetAliveInstancesOf<T>()
method could come at a
performance cost because it would certainly forces the GC to stop all thread to
build the list of alive instances. But all these checks would be done at
development/debug/test time and removed from production code.

 

I imagine that such
method could be hi-jacked by some programmers for awkward use. But the core
part of the framework contains
already plenty of sensitive thinks that can easily be hi-jacked.

Share/Bookmark

This entry was posted in GC. Bookmark the permalink. Follow any comments here with the RSS feed for this post.
  • http://codebetter.com/members/Patrick-Smacchia/default.aspx Patrick Smacchia

    Sacha, I wanted to let you know that I tried your solution and it is working like a charm :o) Thanks!

  • http://www.cuttingedge.it/blogs/steven Steven

    Indeed integrating SOS is a bit complicated. But when we combine the ideas given by others, here is what will work:
    1. Create a base class that will register itself on construction and deregister itself on finalization to a GarbageTracker.
    2. Let types you want to track inherit from that base class (but only in test builds).
    3. Implement a GarbageTracker that manages a list of WeakReference objects that point to the registered classes.

    Here is a working example. It only seems to work in release builds and when ran outside the VS IDE.

    Usage:

    public class MyTrackableClass
    #if TEST
    : TrackableObject
    #endif
    {
    }

    static void Main()
    {
    var c = new MyTrackableClass();

    int countBeforeCollect = GarbageTracker.GetAliveInstancesOf().Length;
    Console.WriteLine(“alive instances: ” + countBeforeCollect);

    GC.Collect(2);
    GC.WaitForPendingFinalizers();
    GC.Collect(2);
    GC.WaitForPendingFinalizers();

    int countAfterCollect = GarbageTracker.GetAliveInstancesOf().Length;
    Console.WriteLine(“alive instances: ” + countAfterCollect);
    }

    // Implementation

    public abstract class TrackableObject
    {
    private int reference;

    public TrackableObject()
    {
    this.reference = GarbageTracker.StartTracking(this);
    }

    ~TrackableObject()
    {
    if (this.reference != 0)
    {
    GarbageTracker.StopTracking(reference);
    this.reference = 0;
    }
    }
    }

    public static class GarbageTracker
    {
    private static readonly Dictionary references;
    private static readonly object locker = new object();

    static GarbageTracker()
    {
    references = new Dictionary();
    }

    public static T[] GetAliveInstancesOf() where T : TrackableObject
    {
    lock (locker)
    {
    var instances =
    from reference in references.Values
    let instance = reference.TrackableObject
    where instance is T
    select (T)instance;

    return instances.ToArray();
    }
    }

    internal static int StartTracking(TrackableObject obj)
    {
    TrackableObjectReference reference = new TrackableObjectReference(obj);

    Console.WriteLine(“Starting tracking: ” + reference.Id + “, ” + obj.ToString());

    lock (locker)
    {
    references[reference.Id] = reference;
    }

    return reference.Id;
    }

    internal static void StopTracking(int reference)
    {
    lock (locker)
    {
    references.Remove(reference);
    }

    Console.WriteLine(“Stopped tracking: ” + reference);
    }

    class TrackableObjectReference
    {
    private static int GlobalCount = 0;
    private WeakReference weakReference;
    private readonly int id;

    public TrackableObjectReference(TrackableObject obj)
    {
    this.id = Interlocked.Increment(ref GlobalCount);
    this.weakReference = new WeakReference(obj);
    }

    public TrackableObject TrackableObject
    {
    get { return (TrackableObject)this.weakReference.Target; }
    }

    public int Id { get { return this.id; } }
    }
    }

  • http://codebetter.com/members/Patrick-Smacchia/default.aspx Patrick Smacchia

    Sacha, cool :o)

    Steven, that sounds possible but pretty tricky. I never harnessed SOS from my own code. If you get any way to get this it would be appreciated.

  • http://blogs.microsoft.co.il/blogs/sasha Sasha Goldshtein

    Patrick, it doesn’t have to be a base class. It’s just more convenient that way. :-)

  • http://www.cuttingedge.it/blogs/steven Steven

    Just thinking out loud, but can’t you load communicate with SOS (or load it in process) to analyze (in real-time) your .NET process and use the information supplied by SOS to determine any memory leaks?

  • http://codebetter.com/members/Patrick-Smacchia/default.aspx Patrick Smacchia

    Sasha, indeed WeakReference seems to be the good solution as long as it is not proposed by default be the .NET Fx.

    The drawback of the implementation is that it requires InstanceTracker to be a base class, which doesn’t work in the general case. But still it is better than nothing and the community can benefit from your effort. Thanks!

  • http://smellegantcode.wordpress.com/ Daniel Earwicker

    “your solution would be to maintain a static-private-debugonly collection of all alive object of class C not yet finalized?”

    Definitely NOT! As you say, that would keep the objects alive and would serve no purpose. (I don’t know but maybe Sasha is thinking of a separate problem altogether?)

    See above – my solution is to use a static *counter*, not a list. The counter’s current value is the count of the number of instances in existence. It doesn’t let you examine the objects, but it does let you write a test that asserts how many objects exist.

    However, if you want to be able to examine the objects, you could (with a lot of care) keep them in a list of weak references – see http://msdn.microsoft.com/en-us/library/system.weakreference.aspx

  • http://judahgabriel.blogspot.com Judah Himango

    Here here! This is something that would be really helpful in debugging scenarios and help cut down memory leaks in managed apps.

  • http://blogs.microsoft.co.il/blogs/sasha Sasha Goldshtein
  • David Thibault
  • http://codebetter.com/members/Patrick-Smacchia/default.aspx Patrick Smacchia

    Sacha, Daniel, your solution would be to maintain a static-private-debugonly collection of all alive object of class C not yet finalized?

    This is indeed a good idea and the main drawback is indeed that it is very intrusive (although it shouldn’t introduce bias in the behavior between debug/release, it only bias performances).

    ————

    Oups! Afterthough the idea simply doesn’t work :o)  If your instances of class C are referenced by a static collection, they will never get a chance to be finalized!

    The idea suggested is good to count the number of instances alive but doesn’t let us iterate on alive instances.

  • http://blogs.microsoft.co.il/blogs/sasha Sasha Goldshtein

    If you really need something like that, you can bake it into your objects using a finalizer that is compiled only into the Debug build and a static list that threads all the objects of the same type together.

  • http://smellegantcode.wordpress.com/ Daniel Earwicker

    I have some unit tests that do this – I just have a global counter that I increment in the constructor, and then decrement it in a finalizer (you have to use interlocked operations as the finalizers run in another thread). Then you do:

    GC.Collect();
    GC.WaitForPendingFinalizers();

    It’s the execution of the finalizers that gives you the hook you need into how many instances still exist.

    The downside is you have to put some debug-only code into the objects you want to account for, and the more complex this kind of manual instrumentation gets, the riskier it is, because it means that the program you are testing is not the same as the program you are releasing.

    (And you don’t want to leave an unnecessary finalizer on a class in release builds – collecting an object with a finalizer is very much more expensive.)

    But if you find that you commonly introduce bugs that produce leaks and you want to ring an alarm bell as soon as this happens (instead of only finding out the next time you crank up the profiler) then this technique does actually work.

  • http://codebetter.com/members/Patrick-Smacchia/default.aspx Patrick Smacchia

    Kristian, no I don’t want a debugger with manual intervention, I want to use some kind of contract like:
    Debug.Assert(there is no rooted instances of the type T)

    Tommy, I didn’t know that Smalltalk has that feature, good to know :o)

  • http://blog.tcx.be/ Tommy Carlier

    Yet again, Smalltalk was decades ahead of the currently popular platforms.

  • Kristian Freed

    That sounds more like the job for a debugger or similar. I’ve used YourKit profiler (www.yourkit.com) to do things like what you want, it also gives you easy access to dependency graphs to find out what’s keeping your objects alive.