I have an application that was originally written in VB6 that I used a tool to convert to C# with pretty good success from a functional perspective. It processes a high volume of message using lots of small to medium sized COM (C++) objects.
I noticed that a particular test run in the old VB6 app that ran using less than 40M of memory required nearly 900M in the C# app. If I put a GC.Collect() in the inner-most message processing loop of the C# app, it uses the same or less memory as the VB6 app although it is then really, really slow. This leads me to believe there is no "leak" in the absolute sense of the word.
I then ran the C# app through the AQTime memory profiler and it reported that there were an excessive number of COM/C++ objects live on the heap. I hypothesized that this was because the runtime callable wrappers around the COM objects were quite small and never (or rarely) triggered collection in C# even if their referenced COM objects were substantially larger. I thought I could address this by adding explicit Marshal.ReleaseComObject() calls around the COM objects in the C# app. I went and did this in a lot of places where the lifetime of the COM objects was easy to determine. I noticed only a very slight reduction in memory usage.
I am wondering why I did not have better success with this. Looking through the static methods in the Marshal class, I see some that lead me to believe either tha I may be missing some subtlety in the handling of COM references or that my assumption that they are immediately destroyed when the RCW's reference count reaches zero is incorrect.
I would appreciate any suggestions for other approachs that I could try or other things that I may have overlooked or misunderstood.