Why does the destructor for my CLI/CPP ref class not get called (which results in a memory leak)?

Hi:

I have some questions regarding use of gcnew and explicitly calling delete on the created object.
It appears that I have a leak in my code.

Consider the following example.

I have an unmanaged class:
public class FooBar
{
   int x;
   int y;
}

Open in new window


I have a managed class which wraps the unmanaged class:
ref class Foo
{
   private FooBar* pFooBar;

   Foo()
   {
      pFooBar = new FooBar();
   }

   Foo()
   {
      delete pFooBar;
      pFooBar = nullptr;
   }
}

Open in new window



I have  a method which returns a handle to the managed class to a .Net C# client:
public Foo^  GetMeAFoo()
{
   Foo^ foo = gcnew Foo();
   return foo;
}

Open in new window


It's my understanding that since Foo is created on the managed heap that I do not have to explicitly
delete it. Garbage Collection (GC) on the managed side should take care of it.
The problem in my case is that the destructor of my managed class never seems to get called.
The results in the interior pointer to my unmanaged class getting (memory) leaked.

What is the correct way to handle this situation so that the underlying unmanaged class (FooBar) gets
correctly freed (when the managed wrapper class goes out of scope)?

Thanks,
JohnB
LVL 1
jxbmaSoftware ConsultantAsked:
Who is Participating?

[Product update] Infrastructure Analysis Tool is now available with Business Accounts.Learn More

x
I wear a lot of hats...

"The solutions and answers provided on Experts Exchange have been extremely helpful to me over the last few years. I wear a lot of hats - Developer, Database Administrator, Help Desk, etc., so I know a lot of things but not a lot about one thing. Experts Exchange gives me answers from people who do know a lot about one thing, in a easy to use platform." -Todd S.

jkrCommented:
You're not deriving from IDisposable on the .NET side. That seems to be the problem to me...
0
Jacques Bourgeois (James Burger)PresidentCommented:
Managed code is managed in a very different way than unmanaged code is. There is no destructor on a managed class. There is a finalizer. And it is not called when the object goes out of scope, but when the GC requires resources. This could take hours and even days if the computer goes to sleep instead of being shutdown.

The standard in .NET managed classes that use unmanaged objects is to implements the IDisposable interface through a Dispose method that releases the unmanaged objects. This method should ideally be called by the application before the managed object variable goes out of scope in order to release the unmanaged objects.

When appropriate, create a Close method that calls Dispose. Close is more intuitive for some programmers who do not no about Dispose but are used to a Close method from previous experience. Look at classes such as SqlConnection and System.Windows.Forms, that have both. When you call Close, it calls Dispose, that releases the unmanaged resources.

And just in case the programmer who used your class did not care to call your method, also program the finalizer (Finalize method) to call Dispose so that the GC will eventually release everything.

Just lookup the documentation for IDisposable and Finalize for more details on how to deal with unmanaged objects used in your managed classes.
0

Experts Exchange Solution brought to you by

Your issues matter to us.

Facing a tech roadblock? Get the help and guidance you need from experienced professionals who care. Ask your question anytime, anywhere, with no hassle.

Start your 7-day free trial
AndyAinscowFreelance programmer / ConsultantCommented:
>>when the managed wrapper class goes out of scope)?

The problem is probably a lack of understanding.  The garbage collector will call a destructor when the class is removed from memory.  However the GC mechanism does not remove a class from memory when it goes out of scope - it removes it from memory when the memory is required again or when the app is closed.  This is not what one expects compared to unmanaged code - you do not have a memory leak.
0
sarabandeCommented:
you may consider to putting the creation of unmanaged objects into unmanaged code. the managed class then would only hold a 'handle' of the unmanaged object.

typedef FooBar * FBHDL;
class UnmanagedFooBarHandler
{
      std::vector<FooBar*> allbars;
      // private constructor makes it a singleton
      UnmanagedFooBarHandler() {}
      ~UnmanagedFooBarHandler() { while (allbars.size()) { delete allbars[0]; allbars.erase(allbars.begin()); }}
public:
      static UnmanagedFooBarHandler & UFBH() 
     { static UnmanagedFooBarHandler ufbh; return ufbh; }
     FBHDL createFooBar() { allbars.push_back(new FooBar()); return (FBHDL)allbars.back(); }
     void deleteFooBar(FBHDL fb) 
     { std::vector<FooBar*>::iterator f = std::find(allbars.begin(), allbars.end(), fb); 
       if (f != allbars.end()) { delete *f; allbars.erase(f); }
     }
     ...
};

Open in new window


the managed class would create FooBar via handler.

   
FBHDL fb = UFBH().createFooBar();

Open in new window


and delete it after use.

 
UFBH().deleteFooBar(fb);

Open in new window

 

Sara
0
jxbmaSoftware ConsultantAuthor Commented:
I added a finalize to the classes in question and all is well.
0
It's more than this solution.Get answers and train to solve all your tech problems - anytime, anywhere.Try it for free Edge Out The Competitionfor your dream job with proven skills and certifications.Get started today Stand Outas the employee with proven skills.Start learning today for free Move Your Career Forwardwith certification training in the latest technologies.Start your trial today
C++

From novice to tech pro — start learning today.