Partager via


The undetectebility of C++/CLI gcroot<> template references.

In recent builds, we have been having an awful memory leak in our system. Silvio was debugging it and noticed that we had way too many instances of a specific type in our appdomain (where there should have been zero copies of this item when the system was in idle, there were thousands of it).

Now, there are two tools people use to figure out memory leaks - the first is CLRProfiler that gives you a very good and mostly easily understandable view on your allocated objects. The second is windbg that gives you more control but, as is the tradition of Windbg, not very easy way (though, to be honest, this stuff specifically is not really painful). There's a very simple guide on how to use windbg to figure out gc leaks.

In any case, I used CLRProfiler first - sure enough, we had many many objects leaking. CLRProfiler, however, insisted that the leaked references were being held by the root. This usually means that they are a global variable or a variable declaration on a stack. I knew for a fact that this object was neither so I tried digging deeper. I thought maybe the leaking object was being held by another global object and that CLRProfiler was not reporting it properly so I scoured our global objects trying to figure it out.

I then started using Windbg, however, when I asked for a list of root references to the leaking object instance, all I got back was that it was the root of itself (which makes no sense).

So I started going through the code more meticilously. As I dug deeper into our code, I found that the object was being handed down to our interop layer (written in C++/CLI) and that it was being held by a native class by means of using the gcroot<> template. The problem was that we were leaking that native instance! That in turn meant we were leaking the gcroot<> which meant we were leaking the managed object.

Now, since the CLR does not know of native classes in same way it does of managed ones, from its point of view, the object was being held by a root which made detection a little more complex.

In any case, the moral of the story is (other than, clean your objects) is that if you see root references to object that should not have them, you should look for live gcroot<> objects in your C++/CLI projects and see if they are sneakily holding unwanted references.