Search code examples
c#garbage-collectionc++-clidisposefinalize

CLI Native objects getting stuck in gen2 and not garbage collected


I am working on this high frequency production system. There is a C# / CLI layer which calls into a C++ library. What we are observing is that managed objects are going into generation 2 of the garabage collector and getting 'stuck'. Eventually the C# applications grind to a halt as RAM runs out. These managed objects are local objects and should have a very short lifetime. Also they are only referenced once. The C# applications are having to call .Dispose() on all their objects that hold native resources to ensure everything is forcefully deleted. We have quite a few objects so this isn't ideal and from an API perspective is messy. The CLI looks like this:

Field::~Field()
{
    if(m_pField != NULL)
    {
        delete m_pField;
        m_pField = NULL;
    }
    System::GC::SuppressFinalize(this);
}

Field::!Field()
{
    if(m_pField != NULL)
    {
        delete m_pField;
    }
}

Can anyone think why these short lived objects never seem to be collected and free the memory?


Solution

  • The problem is unmanaged objects don't count toward the "memory pressure" value that the GC uses to decide when to do a garbage collection.

    One thing you can do is use GC.AddMemoryPressure( to let the GC know that there is a large unmanaged object associated with your managed wrapper.

    Field::Field()
    {
        //... Other stuff
    
        if(m_pField != NULL)
        {
            m_lSizeOfField = GetSizeOfField(m_pField);
            System::GC::AddMemoryPressure(m_lSizeOfField);
        }
    }
    
    
    Field::~Field()
    {
        //If you had managed objects you would call "delete" here on them.
        //delete m_managedData;
    
        //In C++/CLI if you have unmanged resources just have the dispose method
        // call the finalizer. It is cleaner and easier to maintain.
        // You can't do this in C#
        this->!Field();
    
        //No need to add this next line, C++/CLI does it for you.
        //System::GC::SuppressFinalize(this);
    }
    
    Field::!Field()
    {
        if(m_pField != NULL)
        {
            delete m_pField;
            m_pField = NULL;
            System::GC::RemoveMemoryPressure(m_lSizeOfField);
        }
    }