0

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?

Scott Chamberlain
  • 124,994
  • 33
  • 282
  • 431
  • It is called [`C++/CLI`](https://en.wikipedia.org/wiki/C%2B%2B/CLI) not `C#/CLI`. Make sure to use the correct terms or it will be hard to find info on it when searching for solutions to problems. – Scott Chamberlain Oct 18 '16 at 16:18
  • Not many practical scenarios where a temporary object could get promoted to gen #2. Crystal ball says that the finalizer thread is deadlocked. Have a look-see at it with an unmanaged debugger. – Hans Passant Oct 18 '16 at 16:52
  • Retrospectively these things were going into gen 1 and the analysis we did was incorrect. Scotts reasoning makes sense. – UltimateDRT Oct 20 '16 at 15:23

1 Answers1

3

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);
    }
}
Scott Chamberlain
  • 124,994
  • 33
  • 282
  • 431
  • Is there any alternative? As finding the size of some of our native objects is very tricky and not an exact science. – UltimateDRT Oct 20 '16 at 09:23