Unit Testing and Memory Profiling: Can They Be Combined?
Memory profilers can hardly be called an “everyday tool.” Typically, developers start thinking about profiling their product closer to its release. This approach may work fine until some last-minute issue like a leak or huge memory traffic crushes all your deadlines. The proactive approach would be to profile your app’s functionality on a daily basis, but who’s got the resources to do that? Well, we think there may be a solution.
If you employ unit testing in your development process, it is likely that you regularly run a number of tests on app logic. Now imagine that you can write some special “memory profiling” tests, e.g. a test that identifies leaks by checking memory for objects of particular type, or a test that tracks memory traffic and fails in case the traffic exceeds some threshold. This is exactly what dotMemory Unit framework allows you to do. The framework is distributed as a NuGet package and can be used to perform the following scenarios:
Checking memory for objects of a certain type.
Checking memory traffic.
Getting the difference between memory snapshots.
Saving memory snapshots for further investigation in dotMemory (a standalone .NET memory profiler from JetBrains).
In other words, dotMemory Unit extends your unit testing framework with the functionality of a memory profiler.
IMPORTANT: dotMemory Unit is currently in the EAP (Early Access Program) stage. Please use it for evaluation purposes only!
How It Works
dotMemory Unit is distributed as a NuGet package installed to your test project:
PM> Install-Package JetBrains.DotMemoryUnit -pre
After you install the dotMemory Unit package, ReSharper’s menus for unit tests will include an additional item, Run Unit Tests under dotMemory Unit. In this mode, the test runner will execute dotMemory Unit calls as well as ordinary test logic. If you run a test the ‘normal’ way (without dotMemory Unit support), all dotMemory Unit calls will be ignored.
dotMemory Unit works with all of the unit-testing frameworks supported by ReSharper’s unit test runner including MSTest and NUnit.
A standalone launcher for integrating with CI systems like JetBrains TeamCity is planned for future releases.
Now let’s take a look at some examples to better understand what dotMemory Unit does.
Example 1: Checking for Specific Objects
Let’s start with something simple. One of the most useful cases can be finding a leak by checking memory for objects of a specific type.
A lambda is passed to the
Checkmethod of the static
dotMemoryclass. This method will be called only in case you run the test using Run test under dotMemory Unit.
memoryobject passed to the lambda contains all memory data for the current execution point.
GetObjectsmethod returns a set of objects that match the condition passed in another lambda. This line slices the memory by leaving only objects of the
Assertexpression asserts that there should be 0 objects of the
Note that dotMemory Unit does not force you to use any specific
Assertsyntax. Simply use the syntax of the framework your test is written for. For example, the line in the example uses NUnit syntax but could be easily updated for MSTest:
With dotMemory Unit you can select a set of objects by almost any condition, get the number of objects in this set and their size, and use these data in your assertions.
In the following example, we check for objects in the large object heap:
Example 2: Checking Memory Traffic
The test for checking memory traffic is even simpler. All you need do to is mark the test with the
AssertTraffic attribute. In the example below, we assert that the amount of memory allocated by all the code in
TestMethod1 does not exceed 1,000 bytes.
Example 3: Complex Scenarios for Checking Memory Traffic
If you need to get more complex information about memory traffic (say, check for traffic of objects of a particular type during some specific time interval), you can use a similar approach to the one from the first example. The lambdas passed to the
dotMemory.Check method slice and dice traffic data by various conditions.
To mark time intervals where memory traffic can be analyzed, use checkpoints created by
dotMemory.Check(as you probably guessed, this method simply takes a memory snapshot).
The checkpoint that defines the starting point of the interval is passed to the
For example, this line asserts that the total size of objects implementing the
IFoointerface created in the interval between
memoryCheckPoint2is less than 1,000 bytes.
You can get traffic data for any checkpoint that was set earlier. Thus, this line gets traffic between the current
Example 4: Comparing Snapshots
Like in the ‘standalone’ dotMemory profiler, you can use checkpoints not only to compare traffic but for all kinds of snapshot comparisons. In the example below we assert that no objects from the
MyApp namespace survived garbage collection in the interval between
memoryCheckPoint1 and the second
dotMemory Unit is very flexible and allows you to check almost any aspect of app memory usage. Use “memory” tests in the same way as unit tests on app logic:
After you manually find an issue (such as a leak), write a memory test that covers it.
Write tests for proactive testing – to ensure that new product features do not create any memory issues, like objects left in memory or large traffic.
Thanks for reading and don’t hesitate to try dotMemory Unit EAP on your own! It’s absolutely free, and the only requirement is ReSharper or dotCover installed on your machine.