HOWTO: Force GC.Collect for an application under test from TestComplete

So, recently I've been asked to measure how much memory our application is using. The requirements were as follows:
1) Get current memory
2) Do somethings with an app
3) Get new memory reading

Seems pretty easy, right? Just go to a task manager and write down current memory reading. Then after some go back to our task manager and get new reading. Subtract one from the other and get your delta!

Well, what I didn't take into an account is that if you're testing a .NET application, garbage collection might not have taken place yet and memory that you're getting from task manager might still contain some objects that haven't been completely release yet.

So, the main question that arises from this is "How do you force your application under test (AUT) to call its garbage collection?"
After poking around the Process object of AUT, I stumbled upon an AppDomain object. Inside of that object, I found a field called dotNET which contains references to all of the loaded objects that AUT has a reference to. In particular, since GC is "located" in System.GC, I had to navigate to a System object and inside of it, open GC object. Once you have a handle on your GC object, you can call any method that it exposes.

I realize that whatever I said earlier has probably confused you beyond belief, so, let me show you a command that I'm using in my script.

var memoryUsed = Sys.Process("MyApp").AppDomain("MyApp.exe").dotNET.System.GC.GetTotalMemory(true);
Log.Warning("Memory used after garbage collection is: " + memoryUsed);

If you want this to be a little more flexible, you can write a generic function that would work on any .NET application. Your code will look something similar to this:

function GetMemoryAfterGarbageCollection(processRef) {
  // make sure that process reference is valid
  if (!processRef || !processRef.Exists) {
    Log.Error("GetMemoryAfterGarbageCollection(): 'processRef' either wasn't passed in or does not exist.");
    return -1;
  }
 
  // find an AppDomain object
  var propNames = new Array("ClrClassName");
  var propValues = new Array("AppDomain");
  var appDomain = processRef.FindChild(propNames, propValues, 1);
  if (!appDomain.Exists) {
    Log.Error("GetMemoryAfterGarbageCollection(): Could not find an AppDomain object.");
    return -1;
  }
 
  // make sure that full path is supported
  if (!IsSupported(appDomain, "dotNET")) {
    Log.Error("GetMemoryAfterGarbageCollection(): 'dotNET' property was not found in the AppDomain object.");
    return -1;
  }
 
  // get memory reading
  return appDomain.dotNET.System.GC.GetTotalMemory(true);
}

For the list of available functions and their description, check out this page: System.GC

Comments