4

I wanted to detect whether or not my code generates garbage. So I created the following unit test.

[TestClass]
public class AllocationTest
{
    int[] generationCollections = new int[3];

    [TestMethod]
    public void TestGarbageGeneration()
    {
        generationCollections[0] = GC.CollectionCount(0);
        generationCollections[1] = GC.CollectionCount(1);
        generationCollections[2] = GC.CollectionCount(2);

        // Test for garbage here

        for (int generation = 0; generation < generationCollections.Length; generation++)
        {
            Assert.AreEqual(GC.CollectionCount(generation), generationCollections[generation]);
        }
    }
}

I put the code in question where the "Test for garbage here" comment is and the results are unpredictable. My understanding is that this is due to the fact that GC runs on a separate thread and can be triggered by code other than my test at any time.

I tried GC.Collect to forcefully run collections before and after the test code but then realized that that always increment the collection count, so that test always fails.

Is there a meaningful way to test for garbage in a unit test?

4

2 回答 2

1

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.

于 2013-04-20T21:00:51.837 回答
1

您可以尝试做的是在实际断言之前使用完全相同的逻辑转储 GC 状态

    // do some logic

    // GC.Collect, Thread.Sleep, ...

    currentCollections[0] = GC.CollectionCount(0);
    currentCollections[1] = GC.CollectionCount(1);
    currentCollections[2] = GC.CollectionCount(2);

然后用这些转储值断言(顺便说一句,断言中的第一个参数是预期的,第二个是实际的)

    for (int generation = 0; generation < generationCollections.Length; generation++)
    {
        Assert.AreEqual(generationCollections[generation], currentCollections(generation));
    }

所以这可能适用于大多数情况,但没有办法让 GC 做某事 - 你可能只是要求它做某事,然后相信......

于 2013-04-20T21:02:08.060 回答