2

Consider a class that implements IDisposable, and that has members in such a way that it will never become eligible for garbage collection when it is not disposed. And as it will not be garbage collected, it will not have the chance to use the destructor for cleaning up.

As a result, when it is not disposed (e.g. unreliable users, programming errors), resources will be leaked.

Is there a general approach how such a class can be designed to deal with such a situation or to avoid it?

Example:

using System;

class Program
{
    static void Main(string[] args)
    {
        new Cyclical();
        GC.Collect();
        GC.WaitForPendingFinalizers();
        Console.ReadKey();
    }
}

class Cyclical
{
    public Cyclical()
    {
        timer = new System.Threading.Timer(Callback, null, 0, 1000);
    }

    System.Threading.Timer timer;

    void Callback(object state)
    {
        Console.Write('.');  // do something useful
    }

    ~Cyclical()
    {
        Console.WriteLine("destructor");
    }
}

(Omitted IDisposable to keep example short.) This class uses a Timer to do something useful at certain intervals. It needs a reference to the Timer to avoid it is garbage collected.

Let’s assume the user of that class will not dispose it. As a result of the timer, somewhere some worker thread has a reference to the Cyclical instance via the callback, and as a result, the Cyclical instance will never become eligible for garbage collection, and its destructor will never run, and resources will leak.

In this example, a possible fix (or workaround) could be to use a helper class that receives callbacks from the Timer, and that does not have a reference, but only a WeakReference to the Cyclical instance, which it calls using that WeakReference.

However, in general, is there a design rule for classes like this that need to be disposed to avoid leaking resources?


For the sake of completeness, here the example including IDispose and including a workaround/solution (and with a hopefully less distracting name):

class SomethingWithTimer : IDisposable
{
   public SomethingWithTimer()
   {
      timer = new System.Threading.Timer(StaticCallback,
         new WeakReference<SomethingWithTimer>(this), 0, 1000);
   }

   System.Threading.Timer timer;

   static void StaticCallback(object state)
   {
      WeakReference<SomethingWithTimer> instanceRef
         = (WeakReference<SomethingWithTimer>) state;
      SomethingWithTimer instance;
      if (instanceRef.TryGetTarget(out instance))
         instance.Callback(null);
   }

   void Callback(object state)
   {
      Console.Write('.');  // do something useful
   }

   public void Dispose()
   {
      Dispose(true);
      GC.SuppressFinalize(this);
   }

   protected virtual void Dispose(bool disposing)
   {
      if (disposing)
      {
         Console.WriteLine("dispose");
         timer.Dispose();
      }
   }

   ~SomethingWithTimer()
   {
      Console.WriteLine("destructor");
      Dispose(false);
   }
}
  • If disposed, the timer will be disposed.
  • If not disposed, the object will become eligible for garbage collection.
11
  • Why don't you consider using using? It's guaranteed to call Dispose for implementations of IDisposable on that instance. See here.
    – Neil
    Apr 17, 2014 at 8:32
  • @Neil: My question is about when the user of the class does not dispose it. I cannot force all users of the class to use using.
    – Martin
    Apr 17, 2014 at 8:34
  • 1
    Same could be said for any class then. If the caller doesn't handle your class properly, as they say it's their problem. Most programmers are generally good about ensuring that Streams are closed. Maybe you could consider renaming Cynical as CynicalStream and putting in a Close method and call Dispose yourself within Close.
    – Neil
    Apr 17, 2014 at 8:40
  • @Neil but that won’t help with programmers that are not good about closing streams ;-) However, a stream has a destructor, and that destructor will be called eventually, and that’s the difference here.
    – Martin
    Apr 17, 2014 at 8:44
  • 1
    @Martin with your example code, after pressing any key, the finalizer does get called (I tried it!) - admittedly, AppDomain teardown might be later than you want, but it does happen...
    – AakashM
    Apr 17, 2014 at 12:18

5 Answers 5

2

One approach which can be helpful is to expose wrapper objects to the outside world, but don't hold any strong references to them internally. Either have the internal objects keep weak references to the wrappers, or else have each wrapper hold the only reference anywhere in the world to a finalizable object which in turn holds a reference to the "real" object [I don't like having any outside-world-facing objects implement Finalize, since objects have no control over who can call things like GC.SuppressFinalize() on them]. When all outside-world references to the wrapper object have disappeared, any weak references to the wrapper object will be invalidated [and can be recognized as such], and any finalizable object to which the wrapper held the only reference will run its Finalize method.

For event handlers which are guaranteed to be triggered on some period basis (e.g. timer ticks) I like the WeakReference approach. There's no need to use a finalizer to ensure the timer gets cleaned up; instead, the timer-tick event can notice that the outside-world link has been abandoned and clean itself up. Such an approach may also be workable for things whose only resources are subscriptions to rarely-fired events from long-lived (or static) objects, if those objects have a CheckSubscription event which fires periodically when a subscriber is added (the rate at which those events fire should depend upon the number of subscriptions). Event subscriptions pose a real problem is when an unbounded number of objects may subscribe to an event from a single instance of a long-lived object and be abandoned without unsubscribing; in that case, the number of abandoned-but-uncollectable subscriptions may grow without bound. If 100 instances of a class subscribe to an event from a long-lived object and are subsequently abandoned, and nothing else ever subscribes to that event, memory used by those subscriptions may never get cleaned up, but the quantity of wasted memory would be limited to those 100 subscriptions. Adding more subscriptions would at some point cause the CheckSubscription event to fire, which would in turn cause all the abandoned objects' subscriptions to get canceled.

PS--Another advantage of using weak references is that one can request that a WeakReference remain valid until the object to which it refers is 100% absolutely and irretrievably gone. It's possible for an object to appear abandoned and have its Finalize method scheduled, but then get resurrected and returned to active use even before its Finalize method actually executes; this can happen completely outside the object's control. If code holds a long weak reference to the public wrapper object, and if it makes sure the reference itself remains rooted, it can hold off on performing any cleanup until resurrection becomes impossible.

3

Under normal circumstances, cyclical references are not a problem for the GC, because it can detect them. But here, the whole object graph looks like this (based on looking at the reference source):

Timer object graph

So, the problem isn't just the cycle, it's that you have a static field that references the cycle.

What you could do to fix this is to break the cycle between TimerCallback and Cyclical. In your sample code, that's simple: just changeCallback into a static method. In your real code, breaking the cycle might require more work, like creating a separate class for Callback, or maybe even using WeakReference.

What happens when you do this is that when Cyclical gets GCed, the finalizer of TimerHolder will run, which will stop the timer.

But you should still be careful with this, because the usual caveats for finalizers apply: you never know when will a finalizer run. Especially when you're not allocating much memory, it may take a very long time before it's run.

2
  • I am aware of the caveats you mentioned. Would you recommend implementing such a workaround? Would you call it a “workaround” or a “solution”?
    – Martin
    Apr 17, 2014 at 12:24
  • @Martin I would call this a solution to your (quite unusual) requirements. I doubt you'll find anything that's significantly better.
    – svick
    Apr 17, 2014 at 12:27
2

Consider a class that implements IDisposable, but when it is not disposed by its user, it will not become eligible for garbage collection, thus its destructor won’t run, and resources will be leaked.

Your presumption is incorrect. All classes are disposed and a properly written implementation of IDisposable will not leak resources.

To be absolutely clear,

  • The garbage collector eventually collects all GC memory and does not free non-GC memory. Cyclical references are still collected, when available.
  • IDisposable exists in order to free non-GC memory (and other resources) and has no role in freeing GC memory.

If Dispose is never called then all the GC memory will still be freed eventually. A properly written implementation of IDisposable will ensure that unmanaged resources will be freed too, even if Dispose is not called.

See http://msdn.microsoft.com/en-us/library/system.idisposable.aspx for details. The relevant quote is:

Because the IDisposable.Dispose implementation is called by the consumer of a type when the resources owned by an instance are no longer needed, you should either wrap the managed object in a SafeHandle (the recommended alternative), or you should override Object.Finalize to free unmanaged resources in the event that the consumer forgets to call Dispose.

If you have a reason to believe this is not so, please provide a reference and explanation why.


Two further points. There is no particular reason to expect that cyclical references cannot be garbage collected. Cycles cause a lot of problems for reference counting, but the GC has no such problems. The algorithm has 3 phases (mark, relocate, compact) and it finds live objects by tracing from 3 places (stack roots, GC handles and static data). Cycles that have no root are simply disposed.

There is a problem lurking here that wasn't mentioned. Although the GC guarantees the eventual finalisation of all objects, during which non-GC resources will be released, it does not guarantee the order in which this will happen. If there are order dependencies among the non-GC resources then relying on finalisation may result in leaks. The solution to that involves the proper allocation and management of non-GC resources, which is outside the scope of this question.

4
  • Please see the example I provided in my question. The GC will not free objects of the Cyclical class because they do not become eligible for garbage collection because there still is a reference to them. Thus, the GC will never collect instances of Cyclical, and this is an absolute correct behavior of the GC. IDisposable is just a possibility to get rid of that reference.
    – Martin
    Apr 17, 2014 at 10:45
  • I think you're wrong. The GC is well aware of the possibility of cyclical references, and the algorithm it uses does collect them. If you believe otherwise, please provide a reference.
    – david.pfx
    Apr 17, 2014 at 14:07
  • My question was not about cyclical references. Sorry for naming the example class that way. The reference that prohibits garbage collections is somewhere in the thread pool, cf. svick’s answer.
    – Martin
    Apr 17, 2014 at 14:19
  • "Eventually" is "once the app domain is shut down" in this case. So it's a memory leak if you don't dispose it manually. Apr 17, 2014 at 14:48
0

If your class contains a IDisposable field you'll need to make that also IDisposable so it can be explicitly Dispose its own fields. Or otherwise guarantee the Dispose happens eventually by adding a Finalize method.

Note that using the destructor for cleanup is not enough because they are not guaranteed to run (at least not in java).

class Cyclical: IDisposable
{
    public Cyclical()
    {
        timer = new System.Threading.Timer(Callback, null, 0, 1000);
    }

    System.Threading.Timer timer;

    public void Dispose()
    {
        timer.Dispose();
    }

    protected virtual void Finalize()
    {
        Dispose();
    }

    void Callback(object state)
    {
        Console.Write('.');  // do something useful
    }

    ~Cyclical()
    {
        Console.WriteLine("destructor");
        Dispose();
    }
}
2
  • I left away IDisposable to keep the example short. My question was how to design the class to avoid leaks when Dispose is not called and the desctructor/Finalize cannot run because the object will never become eligible for garbage collection.
    – Martin
    Apr 17, 2014 at 8:12
  • 2
    Although .NET does not guarantee that Finalize is called, under “normal” circumstances it always will be called. Cf. Object.Finalize.
    – Martin
    Apr 17, 2014 at 8:20
0

To do this, first the fundamental rules of .Net garbage collection must be explained. An object is kept alive while any of the following is true:

  • It is referenced in a static field
  • It is referenced on the stack in any thread
  • It is referenced by any object that is kept alive by any of these rules

So, if you have a static field in a class, any object placed in that field will be kept alive until that field is set to null. If you put a collection, array, ect, into that static field, any object in the collection or array will also be kept alive until it's removed from the collection, or the field is set to null.

To answer your question, though:

public class KeepAlive : IDisposable
{
   // Never garbage collected because its static
   private static HashSet<KeepAlive> doNotCollect = new HashSet<KeepAlive>();

   public KeepAlive()
   {
      // Right now this new object is kept alive because it's referenced on the
      // stack

      lock (doNotCollect)
         doNotCollect.Add(this);

      // Now this new object will never be collected until Dispose is called
      // because:
      // - doNotCollect isn't collected because its in a static field
      // - The new object is referenced by doNotCollect
   }

   public void Dispose()
   {
      lock (doNotCollect)
         doNotCollect.Remove(this);
   }
}

The above solution will work because it ensures that the new KeepAlive object is referenced in a static collection until Dispose() is called. One tiny caveat, though: Overriding Equals(), GetHashCode(), or the == operator, can cause problems. Thus, in this situation, you should not alter object equivalence behavior.

But, are you sure that's the behavior you want? It might be better to use the garbage collector to your advantage and warn about the resource leak. For example:

public class WarnWhenLeaked : IDisposable
{
    public void Dispose()
    {
       // Prevent the GC from calling ~WarnWhenLeaked()
       GC.SurpressFinalize(this);
    }

    ~WarnWhenLeaked()
    {
       Console.WriteLine("Leaked Resource!!!");
       this.Dispose();
    }
}

In the above class, when the object is Disposed, the call to the finalizer (~WarnWhenLeaked()) is disabled. Thus, if the object is garbage collected without calling Dispose(), it's able to log a warning, and release the resource.

The above situation might be better than permanently leaking the resource, because it could allow your program to recover from a bug.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.