This is continutaion of earlier article on the same subject, delving into long weak references.
Introduction & backgound
We have seen fundamentals of weak reference in previous article http://www.dotnetfunda.com/articles/article1419-weak-references-in-net-.aspx and we saw a practical example of usage of weak reference. A little excerpt from the said article “A strong reference is a reference pointing to an object created by new operator and the object would reside in the memory as long as the reference is pointing to an object in turn it protects the referenced object from being collected by GC (garbage collector). A weak reference is a reference that doesn’t protect the referenced object from being collected which means the object referenced by weak reference is considered unreachable (or weakly reachable) and so may be collected at any time, more precisely when more memory is in needed. Different languages like java, C#, VB.NET, Python, Perl, Lisp etc support this feature.”
Weak reference are useful for objects which would use a lot of memory and these objects can be recreated easily if they are collected by GC.
To establish a weak reference with an object, you create a WeakReference using the instance of the object to be tracked. One can specify ‘false’ in the constructor if short weak reference is to be used otherwise ‘true’ for long weak reference. One then set the Target property to that object and set the object to null.
Let’s delve more into world of weak references and learn about long and short weak reference.
Description
The target of short weak reference becomes null when it is reclaimed by GC. A long weak reference is retained after the object’s finalize method is called i.e. when generally IDisposable pattern is implemented for unmanaged objects like database connections or file handles. When object doesn’t have Finalize method then the short weak reference functionality is applied. Such weak reference is valid until target is collected which can happen anytime after finalizer is run. This we can see that no object is reclaimed by GC even calling GC.Collect(); in case of long weak reference as Targets finalize method haven’t been called.
Scenario: We have a class named Person and also we need images representing the image of the person. We know Image type implements IDisposable (http://msdn.microsoft.com/en-us/library/system.idisposable.aspx) and hence can be used for long weak reference. Now there would be thousands of records for person which is having associated images. Generally this can be showed in data-grids (grid-views) or tree views where in thumbnail of image can be displayed. Such thumbnail would be prepared from image (Image. GetThumbnailImage() method). As this display may not critical to application i.e. this may not be required at many places and not much of business logic depends on this image display, one could think of using weak reference and here we would be using long. In previous article we employed short weak references.
Example:
Examples are in C# and exercise is done as console application.
public class Person
{
public string FirstName { get; set; }
public string LastName { get; set; }
public int PersonId { get; set; }
public Person(string firstName, string lastName)
{
this.FirstName = firstName;
this.LastName = lastName;
}
public Person(string firstName,string lastName, int ID)
{
this.FirstName = firstName;
this.LastName = lastName;
this.PersonId = ID;
}
public string PersonName
{
get{ return FirstName + " " + LastName;}
}
}
A class meant for holding different objects of persons and images, we would treat it as cache.
/// <summary>
/// The class to hold different objects of Person and behave as cache
/// </summary>
public class ImageCache
{
int regenarationCount = 0;// Track the number of times an object is regenerated after collection by GC
private string personName = string.Empty;
static Dictionary<int, WeakReference> _personsImageCache;// cache for imagedatas
static Dictionary<int, Person> _personsCache;//cache for person objects
public int Count // Returns the number of items in the cache.
{
get { return _personsImageCache.Count; }
}
public int RegenerationCount // Returns the number of times an object had to be regenerated.
{
get { return regenarationCount; }
}
public string PersonName
{
get { return personName; }
}
public Image this[int index]
{
get
{
personName = _personsCache[index].PersonName;
// Obtain an instance of a data object from the personcache of of weak reference objects.
Image imageData = _personsImageCache[index].Target as Image;
if (imageData == null) //If the object was reclaimed, so generate a new one.
{
Console.WriteLine("Regenerate object at {0}: Yes", index);
imageData = Image.FromFile("@C:\\Users\\Public\\Pictures\\Sample Pictures\\Desert.jpg");
regenarationCount++;
}
else // As Object is not reclaimed so can be obtained with the weak reference.
{
Console.WriteLine("Regenerate object at {0}: No", index.ToString());
}
return imageData;
}
}
public ImageCache(int count)
{
_personsImageCache = new Dictionary<int, WeakReference>();
_personsCache = new Dictionary<int, Person>();
// Add data objects with a short weak reference to the cache.
for (int i = 0; i < count; i++)
{
Person person = new Person(i.ToString(), i.ToString(), i);
_personsCache.Add(person.PersonId, person);
//Long weak reference
_personsImageCache.Add(person.PersonId, newWeakReference(Image.FromFile("C:\\Users\\Public\\Pictures\\Sample Pictures\\Desert.jpg"), true));
}
}
}
Console application
static void Main(string[] args)// for long Weakreference demo
{
int cacheSize = 5;//define the cache size. For demonstration purpose, its taken very small.
Random random = new Random();
ImageCache imageCache = new ImageCache(cacheSize);// Create the cache with defined size
string personName;
System.Drawing.Image image;
// GC.Collect() is to be commented for ouput I where in the objects from cache are not collected by GC
//and uncommented for output II
Console.WriteLine("Demonstration of Long weak reference");
Console.WriteLine("In method Main (...), the call to GC.Collect() is commented.");
//GC.Collect();
Console.WriteLine("***************Random Display of objects.***************");
for (int i = 0; i < imageCache.Count; i++)
{
int index = random.Next(imageCache.Count);// Randomly access objects in the cache
image = imageCache[index]; // Access the object by getting a property value
// Console.WriteLine("ImageData Name: " + image.Height.ToString());
personName = imageCache.PersonName;
Console.WriteLine("Person Name: " + personName);
}
double regenPercent = (((imageCache.RegenerationCount) * (100)) / (imageCache.Count));
Console.WriteLine("Cache size: {0}, Regenerated: {1}%", imageCache.Count.ToString(), regenPercent.ToString());
Console.WriteLine("***************************************************************");
Console.ReadLine();
}
Output: Two kinds of output are displayed.
I: GC not collecting the weak references as GC.Collect() is commented
Demonstration of Long weak reference
In method Main (...), the call to GC.Collect() is commented.
***************Random Display of objects.***************
Regenerate object at 4: No
Person Name: 4 4
Regenerate object at 4: No
Person Name: 4 4
Regenerate object at 1: No
Person Name: 1 1
Regenerate object at 3: No
Person Name: 3 3
Regenerate object at 3: No
Person Name: 3 3
Cache size: 5, Regenerated: 0%
***************************************************************
II: GC not collecting the weak references as they are long weak reference and application has gained the strong references
Demonstration of Long weak reference
In method Main (...), the call to GC.Collect() is uncommented.
***************Random Display of objects.***************
Regenerate object at 4: No
Person Name: 4 4
Regenerate object at 4: No
Person Name: 4 4
Regenerate object at 1: No
Person Name: 1 1
Regenerate object at 2: No
Person Name: 2 2
Regenerate object at 4: No
Person Name: 4 4
Cache size: 5, Regenerated: 0%
***************************************************************
Summary & Conclusion
The output clearly demonstrates what happens when one uses the long weak references. The weakly referenced objects are collected by GC (here is specifically called upon for demo but in practical scenarios when needed GC would collect these objects) when they are short weak reference and in case of long weak reference, it is retained even after the object’s Finalize method is called. So there are no regenerations occurred as the application has regained a strong reference to it. One needs to use property “Target” for using weak reference.
- Use long weak references only when necessary as the state of the object is unpredictable after finalization.
- Avoid using weak references to small objects because the pointer itself may be as large or larger.
- Weak references shouldn’t be seen as an automatic solution to memory management problems. Instead, effective caching policy for handling your application's objects could help in better memory management
References: