C# - Get number of references to object
Asked Answered
S

5

20

I'm trying to write a simple Resource Manager for the little hobby game I'm writing. One of the tasks that this resource manager needs to do is unloading unused resources. I can think of doing this in two ways:

  • When an object no longer requires a reference to the resource, it must call a method of the Resource Manager to signify it is no longer using it; or

  • When an object no longer requires a reference to the resource, it simply sets it to null. Then when the Resource Manager is asked to unload unused resources, it gets the reference count (via reflection?) of each resource. If the reference count is one (the Resource Manager will have a reference to the resource), unload the resource.

Is there any way to acheive the second solution in C#? Thanks.

Shopping answered 6/11, 2009 at 9:20 Comment(3)
What will your resource manager do that the garbace collector doesn't?Nidorf
@Nidorf - typically, allow multiple bits of code to re-use the resource (essentially, a cache)Parable
Its for loading images, making sure only one image is loaded at a time (no matter how many things ask for it, only one version can be in memory).Shopping
P
17

It sounds to me that you could just use WeakReference from the resource manager. The GC will do the rest. You'll need to do a little casting, but it will be simple, and will work.

class Manager {
    Dictionary<string, WeakReference> refs =
        new Dictionary<string, WeakReference>();
    public object this[string key] {
        get {
            WeakReference wr;
            if (refs.TryGetValue(key, out wr)) {
                if(wr.IsAlive) return wr.Target;
                refs.Remove(key);
            }
            return null;
        }
        set {
            refs[key] = new WeakReference(value);
        }
    }
}
static void Main() {
    Manager mgr = new Manager();
    var obj = new byte[1024];
    mgr["abc"] = obj;

    GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced);
    Console.WriteLine(mgr["abc"] != null); // true (still ref'd by "obj")

    obj = null;
    GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced);
    Console.WriteLine(mgr["abc"] != null); // false (no remaining refs)
}
Parable answered 6/11, 2009 at 9:25 Comment(5)
I'd do this :) One thing, though: I could be wrong, but I think you may have to get the proper reference out of the dictionary and check if it's null prior to returning it. If you do "if(wr.IsAlive) return wr.Target", you're never taking a strong ref, so IsAlive may return true, but by the time you return the value, wr.Target may have been collected. There's an alternative code sample here here: msdn.microsoft.com/en-us/library/system.weakreference.aspxFeeze
object is strong enough to prevent collection; either it will be null or strong. The caller would have to cast it to make it a typed reference, but that is a different issue...Parable
.NET 4.5 has a generic version of WeakReference, check this out philosophicalgeek.com/2014/08/14/…Vinaya
Isn't whole point of caching to be able to reuse resources even if they are temporarily not required? If cache is cleaned by GC, then does cache do anything except obscuring code? Won't it just pretend that it does something different than GC.Skirr
@AdasLesniak I'm not the enquirer but one use case for this would be to keep the cache under a certain size. Check if the object is currently used - this is done to not cause bugs in your UI and then remove it or leave it be. And yes, basically this is a 'GC' for your own cache.Bubbly
R
37

Couple things. First off, objects are not reference counted; reference counting schemes have the circular reference problem, whereby two objects refer to each other but are otherwise inaccessible, and thereby leak. .NET uses a mark-and-sweep approach which does not use ref counts.

Second, though the suggestion to use a weak reference is not terrible, it's also not a slam dunk. You are building a cache for performance reasons. (I assume that your careful, empirical, realistic research into the performance characteristics of your application has convincingly demonstrated that a caching strategy is necessary in order to achieve acceptable performance; if that's not the case, then you are making these decisions prematurely.) Every cache has to have a POLICY about when it releases its resources, otherwise it's a memory leak.

How do you know that the GC policy and your policy are equivalent policies? The GC was not designed with your specific performance needs in mind. That is, it was designed to release resources that really are garbage, not to achieve any particular performance goal you've got in mind. By delegating the decision to the GC, you give up your ability to tune your cache policy to your performance needs.

Ramillies answered 6/11, 2009 at 15:25 Comment(0)
P
17

It sounds to me that you could just use WeakReference from the resource manager. The GC will do the rest. You'll need to do a little casting, but it will be simple, and will work.

class Manager {
    Dictionary<string, WeakReference> refs =
        new Dictionary<string, WeakReference>();
    public object this[string key] {
        get {
            WeakReference wr;
            if (refs.TryGetValue(key, out wr)) {
                if(wr.IsAlive) return wr.Target;
                refs.Remove(key);
            }
            return null;
        }
        set {
            refs[key] = new WeakReference(value);
        }
    }
}
static void Main() {
    Manager mgr = new Manager();
    var obj = new byte[1024];
    mgr["abc"] = obj;

    GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced);
    Console.WriteLine(mgr["abc"] != null); // true (still ref'd by "obj")

    obj = null;
    GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced);
    Console.WriteLine(mgr["abc"] != null); // false (no remaining refs)
}
Parable answered 6/11, 2009 at 9:25 Comment(5)
I'd do this :) One thing, though: I could be wrong, but I think you may have to get the proper reference out of the dictionary and check if it's null prior to returning it. If you do "if(wr.IsAlive) return wr.Target", you're never taking a strong ref, so IsAlive may return true, but by the time you return the value, wr.Target may have been collected. There's an alternative code sample here here: msdn.microsoft.com/en-us/library/system.weakreference.aspxFeeze
object is strong enough to prevent collection; either it will be null or strong. The caller would have to cast it to make it a typed reference, but that is a different issue...Parable
.NET 4.5 has a generic version of WeakReference, check this out philosophicalgeek.com/2014/08/14/…Vinaya
Isn't whole point of caching to be able to reuse resources even if they are temporarily not required? If cache is cleaned by GC, then does cache do anything except obscuring code? Won't it just pretend that it does something different than GC.Skirr
@AdasLesniak I'm not the enquirer but one use case for this would be to keep the cache under a certain size. Check if the object is currently used - this is done to not cause bugs in your UI and then remove it or leave it be. And yes, basically this is a 'GC' for your own cache.Bubbly
T
3

We already have a resource manager in .NET, called the Garbage collector. So a very efficient approach is to set the references to null and do nothing.

A more direct answer: No, there is no way to get the references to an object.

You may want to study the WeakReference class or use a Caching system.

Taylor answered 6/11, 2009 at 9:23 Comment(0)
P
1

Ensure the resource manager uses WeakReferences to your resources. That way, when no one else is referencing the resources, they will be eligible for garbage collection.

Pattipattie answered 6/11, 2009 at 9:24 Comment(0)
O
0

As already told by other users, what you are trying to achieve is already made by the GC, and can be fine tuned using a WeakReference.

This imply that in managed environments like .NET, java, and so on, this is a non-problem.

Since the low-level framework architecture isolates you from the memory management, if you still need for that kind of functionality, I strongly suggest you to review your own code architecture, cause it would mean that you are doing some sort of bad-practise in memory management

Octameter answered 6/11, 2009 at 10:6 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.