How to Write a Unit Test to Determine Whether an Object Can Be Garbage Collected

How can I write a unit test to determine whether an object can be garbage collected?

This is what I normally do:

[Test]
public void MyTest()
{
WeakReference reference;
new Action(() =>
{
var service = new Service();
// Do things with service that might cause a memory leak...

reference = new WeakReference(service, true);
})();

// Service should have gone out of scope about now,
// so the garbage collector can clean it up
GC.Collect();
GC.WaitForPendingFinalizers();

Assert.IsNull(reference.Target);
}

NB: There are very, very few times where you should call GC.Collect() in a production application. But testing for leaks is one example of where it's appropriate.

Detecting garbage in a Unit Test

You can use WMemoryProfiler to find out how many additional types were created. If you profile your own process you will get all addtional created types + some instances used by WMemoryProfiler to generate the report.

You can work around by using a separate process to monitor your managaed heap or by limiting yourself to only your types. If you leak memory you will see it normally in addtional instances created by you.

  using (var dumper = new InProcessMemoryDumper(false,false))
{
var statOld = dumper.GetMemoryStatistics();

// allocaton code here
var diff = dumper.GetMemoryStatisticsDiff(statOld);

foreach (var diffinst in diff.Where(d => d.InstanceCountDiff > 1))
{
Console.WriteLine("Added {0} {1}", diffinst.TypeName, diffinst.InstanceCountDiff);
}
}

If you are after how much memory temporary objects did use you you will need to use some profiling Api or tools like PerfView which does use ETL traces generaeted by the CLR. For GC you would need to programatically enable specific stuff like his. I think the GCAllocationTick_V1 event would be interesting in your case as well.

If you do keep a reference to your object before you try to get the diff you would get a pretty good understanding how much memory your object graph will consume.

How does the garbage collector work with unit tests?

Garbage collection is a periodic background task. In specific, there's a thread that does nothing but finalize objects that have already been marked as dead. By running one test at a time, you're giving that thread a chance finalize objects so as to close the connections.

How to Tell If an Object Has Been Garbage Collected

According to this:

You normally can’t tell whether an object has been garbage collected by using some reference to the object–because once you have a reference to the object, it won’t be garbage collected.

You can instead create a weak reference to an object using the WeakReference object. The weak reference is one that won’t be counted as a reference, for purposes of garbage collection.

In the code below, we check before and after garbage collection to show that a Dog object is garbage collected.

Dog dog = new Dog("Bowser");

WeakReference dogRef = new WeakReference(dog);
Console.WriteLine(dogRef.IsAlive);

dog = null;
GC.Collect();

Console.WriteLine(dogRef.IsAlive);

Sample Image

Unit Test Garbage Collection

The answer ultimately depends on the unit testing framework you use, but in .NET, none of the three major test frameworks (MSTest, NUnit, xUnit.net) automatically dispose of things. You have to manually ask them to do so.

You could argue that when executing a test suite, the test runner in principle launches a new process, runs all the tests, and then the process exits.

For some implementations of IDisposable, like MemoryStream, that simply means that the memory is reclaimed when the process exits. However, you can't always rely on that, because some disposable types may access out-of-process resources. You could theoretically have objects holding on to memory-mapped-files, named pipes, SQL Server connections, etc. Even if the test process exits, you may leave behind such resources. They'll probably time out sooner or later (like SQL Server connections returning to the pool), but it may slow down your system.

Furthermore, some test runners are attempting very hard to be clever these days, so they reuse one or more processes to be able to run faster, changing your test suite in and out of AppDomains.

So, in the end, unless you have something like a MemoryStream, where you're absolutely certain that it's not a big deal to leave it behind, you should deterministically dispose of your objects in your tests.

However, if you're doing Test-Driven Development, you should adopt the GOOS attitude of listening to your tests. If you write a lot of tests that involve IDisposable objects, you should consider if you can simplify your SUT's API. It depends on what you're doing, but if you write mostly managed code, you shouldn't need IDisposable much, because it's a Leaky Abstraction that leaks that the SUT depends on unmanaged resources..

how can I verify a specific Javascript object will be garbage collected?

You cannot have any javascript code that detects if a specific object can be GCed. The very code that would test it would keep it from being garbage collected because that code would, by definition, have a reference to it and the garbage collector will not GC something that still has a live reference to it.

The things you can do to verify that there is no memory leak:

  1. Devise a test that runs the offending code over and over and measure the total memory usage of the browser to verify that the memory usage it not going up and up and up.

  2. Assign a gigantic property value to the object (e.g. a mongo string) that is so large that you can clearly see if these objects are leaking in total memory usage.

  3. Use various developer tools (which vary by browser) to look at the browser memory usage in finer detail.

c# Check Garbage Collection

Is there any way to check what classes are collected by GC and what are not?

No, because to check on an object you'd still need a reference to it and that would prevent its collection.

You can build an administration with WeakReferences but that would be awkward and costly. Certainly not feasible for all objects.

More important, you shouldn't be needing this. If you think you do, analyze that thought train.

How can I unit test GC?

The likely best option is to ensure your loaded jars are loaded by a specific class loader, and then to discard that class loader (after discarding all the objects).

As far as unit testing the unloading, if you go with this option, you need to extend your testing framework and customized class loaders to have a "create class loader on demand" flag. Then you load the class once with the flag on, discard the class loader, and attempt to load the class again with the flag off. If the class is truly not reachable, the second attempt should throw a class not found exception. You then wrap your unit tests to pass if they fall into the exception, and fail if they succeed in hitting the line after the second load attempt.

If you are disposed to use more than pure-Java tools, an OSGi container might be a consideration. Most of the established OSGi container implementations explicitly test class unloading.

Different Garbage Collection behavior between Console Application and Unit Test Method

If the following code was extracted to a separate method, the test would be more likely to behave as you expected. Edit: Note that the wording of the C# language specification does not require this test to pass, even if you extract the code to a separate method.

        {
EventedObject publisher = new EventedObject();
publisher.DoIt += subscriber.Yeah;
}

The specification allows but does not require that publisher be eligible for GC immediately at the end of this block, so you should not write code in such a way that you are assuming it can be collected here.

Edit: from ECMA-334 (C# language specification) §10.9 Automatic memory management (emphasis mine)

If no part of the object can be accessed by any possible continuation of execution, other than the running of finalizers, the object is considered no longer in use and it becomes eligible for finalization. [Note: Implementations might choose to analyze code to determine which references to an object can be used in the future. For instance, if a local variable that is in scope is the only existing reference to an object, but that local variable is never referred to in any possible continuation of execution from the current execution point in the procedure, an implementation might (but is not required to) treat the object as no longer in use. end note]



Related Topics



Leave a reply



Submit