B3ret
B3ret

Reputation: 618

How to get notified if a object is garbage collected?

Short version:

For a cache class I need to get notified if an object is garbage collected (to remove the according entries from my cache). What is the best way to do so? Sending an event from the destructor?

Long version:

I am writing a cacher/memoizer for functions that take one huge parameter-tree object and many small value type parameters, e.g.,

double myFunc(HugeParTree parTree, int dynPar1, double dynPar2)

I want to cache these functions in the following way:

Code looks like this right now (for one value parameter):

public class CachedFunction1ObsPar1Par<TRet, TObsPar1, TPar1>
        where TObsPar1 : IObservable, IProvideGUID
    {
        public delegate TRet ValueCalculator(TObsPar1 obsPar1, TPar1 par1);

        public CachedFunction1ObsPar1Par(ValueCalculator calc)
        {
            _calc = calc;
        }


        #region members

        private ValueCalculator _calc;

        private Dictionary<Guid, Dictionary<TPar1, TRet>> _cache = 
            new Dictionary<Guid, Dictionary<TPar1,TRet>>();

        #endregion


        public TRet value(TObsPar1 obsPar1, TPar1 par1)
        {
            TRet result;
            bool cacheHit = checkCache(obsPar1, par1, out result);

            if (cacheHit)
            {
                Debug.Assert(result.Equals(_calc(obsPar1, par1)));
                return result;
            }
            else
            {
                result = _calc(obsPar1, par1);
                _cache[obsPar1.GUID].Add(par1, result);
                return result;
            }
        }

        private bool checkCache(TObsPar1 obsPar1, TPar1 par1, out TRet result)
        {
            if (!_cache.ContainsKey(obsPar1.GUID))
            {
                _cache.Add(obsPar1.GUID, new Dictionary<TPar1, TRet>());
                obsPar1._changed += this.invalidateCache;
            }

            Dictionary<TPar1, TRet> guidCache = _cache[obsPar1.GUID];

            bool success = guidCache.TryGetValue(par1, out result);

            return success;
        }

        private void invalidateCache(object sender)
        {
            TObsPar1 obsPar = (TObsPar1)sender;

            _cache.Remove(obsPar.GUID);

            obsPar._changed -= this.invalidateCache;
        }
    }

I haven't tested this yet, as I still have the problem that cache entries never get removed after the according parTree is not used any more. I'd love a synchronous solution without repeated "scans" for very old cache entries.

Upvotes: 2

Views: 1771

Answers (3)

Manish Basantani
Manish Basantani

Reputation: 17509

Henk already mentioned the flaw in your requirement.

But, just to answer your question. To know when an object is being garbage collected you can write a destructor for that object.

~YourClass();

As per MSDN:

This method is automatically called after an object becomes inaccessible

Though it's never recommended to rely on GC or destructor.

Upvotes: 2

Emond
Emond

Reputation: 50692

You could define an interface 'ICacheable' that must be implemented by the objects in the cache. In a method of the interface RemoveFromCache() you could search the cache for its child objects and remove them.

When you remove an item from the cache, test it for the interface and call RemoveFromCache().

This is similar to IDisposable.

Garbage collection is not something to count on because you never know when it will run.

Upvotes: 1

Henk Holterman
Henk Holterman

Reputation: 273784

For a cache class I need to get notified if an object is garbage collected (to remove the according entries from my cache). What is the best way to do so? Sending an event from the destructor?

If your cache holds normal (strong) references the items will never be collected.

If your cache holds WeakReferences you do not have to remove anything.

Upvotes: 8

Related Questions