MSXML heaps not being released

MSXML uses a garbage collection technique, which allows it to delay releasing resources. There is more informatoin about this on Understanding the MSXML garbage collection mechanism, which gives a nice introduction.

As part of this mechanism, MSXML actually creates and makes use of a number of heaps. Some of these heaps may or may not be used, depending on how different threads call into the library and other factors.

I have recently heard from folks that thought that MSXML was not releasing these private heaps, even after they tried calling CoFreeUnusedLibraries. The catch is that they had a very small repro case; by default, COM won't immediately release a library, but instead give it a little time to clean up after itself in case it has background worker threads. CoFreeUnusedLibrariesEx provides more information on this topic.

This little sample program shows what's going on under the covers - you'll see that the number of heaps goes down when MSXML is released, and remains stable in different init/load/unload/uninit iterations. This is just a toy sample - there is no error handling, and using CoFreeUnusedLibrariesEx to force DLL unloading is a very bad idea - the default behavior of CoFreeUnusedLibraries is a much safer approach and won't interfere with other libraries which might require the clean-up, and is generally more performant than aggressively cleaning up resources that might come in handy in a short bit.

Sample toy:

#include <windows.h>
#include <iostream>
#import "msxml6.dll"

void show_heap_count(const wchar_t* message)
{
    DWORD count = GetProcessHeaps(0, NULL);
    std::wcout << message << count << std::endl;
}

void mess_with_heaps()
{
    show_heap_count(L"We start off with heap count: ");

    CoInitialize(NULL);
    {
        show_heap_count(L"After initializing COM: ");
        MSXML2::IXMLDOMDocumentPtr the_document;
        the_document.CreateInstance(__uuidof(MSXML2::DOMDocument60));
        show_heap_count(L"After creating a document: ");
        the_document->async = VARIANT_FALSE;
        the_document->loadXML(L"<a>text</a>");
        show_heap_count(L"After loading some XML: ");
    }

    show_heap_count(L"After releasing the document: ");
    CoFreeUnusedLibrariesEx(0, 0);
    show_heap_count(L"After freeing unused libraries: ");
    CoUninitialize();
    show_heap_count(L"After uninitializing COM: ");
}

void main()
{
    for (int i = 0; i < 3; i++)
    {
        std::wcout << L"MESS ROUND #" << (i + 1) << std::endl;
        mess_with_heaps();
    }
}

Sample output (your mileage may vary):

MESS ROUND #1
We start off with heap count: 5
After initializing COM: 6
After creating a document: 12
After loading some XML: 12
After releasing the document: 12
After freeing unused libraries: 7
After uninitializing COM: 7

MESS ROUND #2
We start off with heap count: 7
After initializing COM: 7
After creating a document: 12
After loading some XML: 12
After releasing the document: 12
After freeing unused libraries: 7
After uninitializing COM: 7

MESS ROUND #3
We start off with heap count: 7
After initializing COM: 7
After creating a document: 12
After loading some XML: 12
After releasing the document: 12
After freeing unused libraries: 7
After uninitializing COM: 7

Note that if the heaps are never, ever released, it might be because the application still has a reference to an MSXML object - in that case, you'll see that the module is never unloaded, and of course its resources are never released. 

Enjoy!

This posting is provided "AS IS" with no warranties, and confers no rights. Use of included script samples are subject to the terms specified at https://www.microsoft.com/info/cpyright.htm.