Force a Garbage Collection
Scenario/Problem: | You think you need to force a garbage collection. |
Solution: | The
short answer is, don’t. You are unlikely to outguess the
garbage-collection system for efficiency. That said, if you really know
what you’re doing and you’re going to measure the effects, here’s how to
do it:
|
One
reason why people force a collection is that they’re beginning a
performance-sensitive operation that they don’t want interrupted by
garbage collection. However, the garbage collection system in .NET 4 has
undergone major revision from previous versions and now does more
collections in the background, so the need to do forced collections
should be lessened.
Create a Cache That Still Allows Garbage Collection
Scenario/Problem: | You want to cache objects (say, from a database) but still allow them to be garbage collected when necessary. |
Solution: | The problem is that just holding a reference to an object prevents it from being garbage collected. Enter WeakReference. |
This simple Cache class uses WeakReference objects to store the actual values, allowing them to be garbage collected as needed.
public class Cache<TKey, TValue> where TValue:class
{
//store WeakReference instead of TValue,
//so they can be garbage collected
private Dictionary<TKey, WeakReference> _cache =
new Dictionary<TKey, WeakReference>();
public void Add(TKey key, TValue value)
{
_cache[key] = new WeakReference(value);
}
public void Clear()
{
_cache.Clear();
}
//since dead WeakReference objects could accumulate,
//you may want to clear them out occasionally
public void ClearDeadReferences()
{
foreach (KeyValuePair<TKey, WeakReference> item in _cache)
{
if (!item.Value.IsAlive)
{
_cache.Remove(item.Key);
}
}
}
public TValue GetObject(TKey key)
{
WeakReference reference = null;
if (_cache.TryGetValue(key, out reference))
{
/* Don't check IsAlive first because the
* GC could kick in right after anyway.
* Just retrieve the value and cast it. It will be null
* if the object was already collected.
* If you successfully get the Target value,
* then you'll create a strong reference, and prevent
* it from being garbage collected.
*/
return reference.Target as TValue;
}
return null;
}
}
Note
Weak
references should never be used for small items, and you should not
rely on them as a crutch to solve memory problems for you. They are best
used for items that can use a lot of memory, but are easily recreated
as needed, such as in cache situations where it would be nice if the
object were still around in memory, but you still want it to be garbage
collected eventually.
This test program shows how it could be used:
class Program
{
class Book
{
public int Id { get; set; }
public string Title { get; set; }
public string Author { get; set; }
};
static void Main(string[] args)
{
Cache<int, Book> bookCache = new Cache<int, Book>();
Random rand = new Random();
int numBooks = 100;
//add books to cache
for (int i=0;i<numBooks;++i)
{
bookCache.Add(i, GetBookFromDB(i));
}
//lookup random books and track cache misses
Console.WriteLine("Looking up books...hit any key to stop");
long lookups = 0, misses = 0;
while (!Console.KeyAvailable)
{
++lookups;
int id = rand.Next(0, numBooks);
Book book = bookCache.GetObject(id);
if (book == null)
{
++misses;
book = GetBookFromDB(id);
}
else
{
//add a little memory pressure to increase
//the chances of a GC
GC.AddMemoryPressure(100);
}
bookCache.Add(id, book);
}
Console.ReadKey();
Console.WriteLine("{0:N0} lookups, {1:N0} misses",
lookups, misses);
Console.ReadLine();
}
static Book GetBookFromDB(int id)
{
//simulate some database access
return new Book { Id = id,
Title = "Book" + id,
Author = "Author" + id };
}
}