One of the strongest areas of .NET is interoping which allows .NET application to take advantage of legacy code whether it is written in the form of C DLLs or COM objects.
In this post I will talk about the Runtime Callable Wrapper (RCW) which is the .NET wrapper around COM objects when they are being accessed from .NET application.
Before we’ll discuss the internals of the RCW, we should first see how can we create it.
How can a .NET application access COM?
There are 3 methods a .NET application can access a COM object:
- Use the CreateObject function (only available in VB.NET)
- Add a reference to the COM object from Visual Studio .NET which will create an interop assembly automatically for you.
- Manually create an interop assembly and add a reference to it.
While option 2 and 3 are similar in result there are a few side effects on strong naming and namespaces which affects certain things. I will discuss about it later on.
How does an RCW object works?
The RCW is a .NET object which is a wrapper on top of a COM object. It is the bridge between the Garbage Collection approach of .NET and the reference counted approach of COM.
It is treated as a pure .NET garbage collected object which means it will be collected only when no one is referencing it.
Since there is no effective way of marrying the reference counted way and the garbage collected way what the RCW actaully does is always hold a reference of 1 to the COM object (excluding the cases where an RCW is marshalled across AppDomains, in which case its reference count is upped by one). This means that what will determine if the COM object lives or dies is the whether its RCW is alive (which in garbage collection words means its reachable from another .NET object or it is rooted).
An RCW also has a finalizer in which the COM object is actually being dereferenced and destroyed. This means that it takes at least 2 Garbage Collections until the COM object actually dies.
Important things we should all remember about RCWs:
Having an RCW control the death of underlying COM object means that instead of having the deterministic and immediate destruction model of referencing counting we have an undeterministic garbage collection and that is something we should keep an eye on.
The RCW itself is very light so having a lot of RCWs alive will not affect the size of the GC heaps too much, but it will affect the private bytes.
How to detect RCW “leaks”?
RCWs leaks are actually RCWs that are referenced and never released. While it is similar to finding leaking .NET objects that are still being referenced it has other impacts on the system.
As I said earlier, RCWs are light, this means the underlying COM object takes up memory in the default NT heap or in some other custom heap. If its not getting freed it adds presure on the Virtual Memory, since now the native memory takes up more and may take parts in the virtual memory instead of letting the GC reserve them.
Rembmer I said there are a few methods of adding COM objects to your .NET project? The main reason I listed them was due to the fact that each method of adding them to the project will make the objects appear a bit differently in WinDbg.
Option 1 – CreateObject – will make the RCWs appear of type System.__ComObject.
Option 2 – Adding a direct reference to the COM object – will make the RCWs appear as Interop.XXX.ComClassName where XXX is the name of the COM dll and ComClassName is the name of the COM class as it appears in the type library.
Option 3- Manually creating an Interop will make them appear like Option 2 if you did not change the namespace, or as the namespace that was chosen during the type library import using tlbimp.exe utility.
So, how can we actually detect them? There are two useful methods for that.
Using WinDbg to detect RCWs leaks
This method can work on a live debug or with memory dumps. The main technique is:
- Perfrom some operations that allocates RCWs.
- Take a memory snapshot or list all objects of that type that are currently alive.
- Run a GC
- Do step 2 again.
If you took memory dumps, all that is left now is to compare the list of object.
How to list the objects? Simply use !dumpheap -type XXX where XXX is the type of RCWs you use (see the 3 options above to see the names of RCWs object you can expect).
Using LeakDiag to detect RCWs leaks
LeakDiag is a tool developed in Microsoft to detect unmanaged memory leaks. It can track and give you the exact size of the unmanaged memory leak as well as the complete stack trace of the code that allocated that object.
Do use LeakDiag you need to attach it to a running process and perform the same technique as mentioned above with WinDbg. When you click on “Log” in LeakDiag you will be able to get an XML log file that will tell you the exact call stack of the leaking memory and you will be able to identify the call stack that leads to the exact COM object.
NOTE: Make sure you have the correct symbols for your COM objects and that you set up a correct symbols path to those symbols prior to attaching LeakDiag to the process.
You can download LeakDiag from here. Although this is not the latest version, it is still handy.
There is another handy utility (which is written in .NET) that parses the XML log files of LeakDiag called LDGrapher that you can use to view a little bit more easily the LeakDiag logs.
You can download LDGrapher from here (this is also not the latest version).
I plan to write in greater detail about LeakDiag and LDGrapher in a future post.
I’ll try and see if I can convine some of the guys at MS to release LeakDiag and LDGrapher like they released DebugDiag. I’ll keep you posted on those efforts here in this blog.