none
Wrappers/Abandoned objects filling up heap and slowing down app RRS feed

  • Question

  • We have a C++ library (No MFC, No ATL) that provides some core functionality to our .NET application. The C++ DLL is used by SWIG to generate a C# assembly that can be used to access its classes/methods using PInvoke. This C# assembly is used in our .NET application to use the functionality inside C++ DLL.

    In my code, I call a method (GetListOfList) that returns a List<List<SomeClass>>. This SomeClass is a .NET wrapper class that wraps the actual C++ class and is Disposable. When I call Dispose() on this .NET class it calls a delete method inside the C++ class using PINVOKE and then calls GC.SuppressFinalize(this). All good for this class but this SomeClass also contains references to other Disposable classes ClassA and ClassB, accessible as properties returning a List<ClassA> or List<ClassB>. ClassA and ClassB cannot exist outside of SomeClass.

    Question1: When a List<List<SomeClass>> is returned from GetListOfList() method, does SomeClass contains .NET wrapper instances for ClassA and ClassB? I think YES and this makes SomeClass a heavyweight and the List<List<SomeClass>> even heavier. The reason I am asking this is because SomeClass::Dispose() frees up the unmanaged memory for ClassA and ClassB but does nothing with the .NET wrappers. IMO SomeClass::Dispose() should class ::Dispose() on every instance of ClassA and ClassB so that these instances can call their own GC.SuppressFinalize(this) and avoid abandoned objects.

    Here a similar question I posted on SO: http://stackoverflow.com/questions/12506728/why-calling-gc-collect-is-speeding-things-up

    This loop is slowing down the whole application but everything seems to work fine if I put a GC.Collect() inside the loop. I want to know the real problem here which is causing this behavior and any ideas on what could be done to avoid it.


    Click the 'Vote as Helpful' arrow if this post was helpful.

    Tuesday, October 9, 2012 1:11 PM

All replies

  • I added from my first posting a IDisposable function to the class.  Does the example below help you?  I'm not sure if calss autoimatically will release all the memory so you can write you own dispose function. 

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    namespace ConsoleApplication1
    {
        class widget : IDisposable
        {
            void System.IDisposable.Dispose()
            {
                //add disposible code here
            }
        }
        class subwidget_A : widget
        {
        }
        class subwidget_B : widget
        {
        }
        class Program
        {
            static void Main(string[] args)
            {
                widget widgetA = new subwidget_A();
                widget widgetB = new subwidget_B();
                if (widgetA.GetType() == typeof(subwidget_A))
                {
                }
            }
        }
    }

    Tuesday, October 9, 2012 1:42 PM
  • The structure is basically like:

    class SomeClass : IDisposable
    {
        public List<ClassA> ListA { get; private set; }
        public List<ClassB> ListB { get; private set; }
    
        public void Dispose()
        {
            ...PINVOKE to delete unmanaged memory
            GC.SuppressFinalize(this);
        }
    }
    
    class ClassA : IDisposable
    {
        ...some data structures
    }
    
    class ClassB : IDisposable
    {
        ...some data structures
    }
    


    Click the 'Vote as Helpful' arrow if this post was helpful.

    Tuesday, October 9, 2012 1:54 PM
  • Actually the disposable pattern includes a destructor:

    public class MyClass : IDisposable
    {
        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }
        protected virtual void Dispose(bool disposing)
        {
            if (disposing)
            {
                //Dispose of managed objects that may be contained by the object.
            }
            //Dispose of unmanaged resources here.
        }
        //The destructor:
        ~MyClass()
        {
            Dispose(false);
        }
    }


    Jose R. MCP
    Code Samples


    • Edited by webJose Tuesday, October 9, 2012 2:23 PM
    Tuesday, October 9, 2012 2:20 PM
  • Yes, actually my question is not about IDisposable pattern, it is implemented by SWIG automatically while generating the .NET wrapper classes. The thing I want to know is if thousands of .NET wrappers class instances can actually slow down a application and how to avoid this kind of scenario.

    From you example, I infer that when Dispose is called manually, it should dipose all the contained objects i.e. it should call Dispose() on all the IDisposable class instances inside it. In my class that would be like calling Dispose(true) on all instances of ClassA and ClassB in a loop inside SomeClass::Dispose(bool). right?


    Click the 'Vote as Helpful' arrow if this post was helpful.

    Tuesday, October 9, 2012 2:47 PM
  • My problem mainly focuses on the .NET wrapper class instances of contained classes (ClassA and ClassB). I am not sure how your example relates to that.

    Click the 'Vote as Helpful' arrow if this post was helpful.

    Tuesday, October 9, 2012 2:49 PM
  • An explicit call to Dispose() must also dispose contained managed objects either by nulling the pointer variables if they aren't IDisposable, or by calling Dispose() + nulling the pointers.

    The destructor of a managed class is called by the GC on abandoned objects.  At this point the GC may or may not have already collected the contained managed objects so there's no guarantee those still exist and therefore during destruction one only disposes of unmanaged resources because at this point the GC is promising he'll collect (or may have already collected) any other managed resources contained by the object.

    So in a nutshell:  Dispose() of managed objects contained and owned by the object on explicit calls to Dispose().  Explicit calls to Dispose() happen when Dispose() is called (obviously) and by the using() statement.


    Jose R. MCP
    Code Samples

    Tuesday, October 9, 2012 2:52 PM
  • Thanks Jose. Is it a known problem that a large number of abandoned objects can slow down a .NET application? Loop speeds up if I call GC.Collect() but I don't want to do that as it does not seem right to call collect inside a loop.

    Click the 'Vote as Helpful' arrow if this post was helpful.

    Wednesday, October 10, 2012 5:02 AM
  • Sorry, I don't have any data at hand to give you a solid answer to that question.

    Jose R. MCP
    Code Samples

    Wednesday, October 10, 2012 5:08 AM