While running down a suspected memory leak inside a host process it became necessary to identify what objects were on the heap before our messages flowed through our BizTalk system, then force a garbage collection, and review what was on the heap after a garbage collection.
This was to verify that resources used during the processing of the messages were cleaned up as expected.
There are three components to making this work.
- a message schema
- an orchestration
- and a .NET component that can be called by the orchestration from an expression shape
Typically after a recycle of the BizTalk host instance that our resources are running in, take a memory snapshot using ADplus from the Debugging Tools for Windows. This is the base line for comparision after running the messages in question through the system. Its a good idea to have known test data available as you don't want just any garbage running through as it will make identifying resources that are out of place more difficult.
Once you've run the test data through the system, and have verified with BizTalk Healk and Activity Tracking (HAT) that those messages, and service instances, have processed to completion, take another memory snap to see whats on the heap. Again, this is mainly for comparison analysis later.
Drop in the GCMessage instance, which will force a garbage collection to occur, presumably collecting all the objects that were in use by your BizTalk artifcats. Typically, we just want a generation 2 heap collection, as that will also collect all the objects from the heap prior to that. If you know that you have objects that require finalization, then drop the message in a second time (snapping memory in between messages).
Finally, snap the memory one last time and see whats left on the heap. This can give you a solid indication of whether or not your objects and resources are being cleaned up appropriately.
Here is a summary of the solution moving parts:
We use a message instance to control when we want the garbage collections to occur. The schema itself is simplistic; here is a sample instance:
<GCMessage xmlns="http://shobu.schemas/1.0/GC">
<heap>2</heap>
</GCMessage>
The orchestration is not represented here, but it is necessary to use a distinguished property for the heap element so that its value can be accessed from an expression shape inside the BizTalk orchestration designer. The expression shape is used to invoke a method on the helper class, GCWrapper (below).
Here is the sample .NET component that is used to force a GC from within the orchestration. This is packaged in a class library and must be signed with a strong name so that it can be placed in the global assembly cache (GAC).
using System;
namespace SHOBU.BizTalk
{
/// <summary>
/// Utility class to wrap GC functions. Enables the invoking of .NET
/// code from a BizTalk orchestration.
/// </summary>
///
[Serializable]
public class GCWrapper
{
public GCWrapper()
{
}
/// <summary>
/// Forces a garbage collection of a specific generation heap.
///
/// heap values:
///
/// 0 - generation 0
/// 1 - generation 1
/// 2 - generation 2
///
/// Passing a value of 2 will force a garbage collection of all
/// managed heaps.
/// </summary>
/// <param name="heap">The generation heap to collect.</param>
public static void InvokeGC(int heap)
{
System.GC.Collect(heap);
}
}
}
Of course, the solution needs to be built and deployed to the BizTalk server in order for it to work.