Friday, April 15, 2011

Does C# clean up C++ allocated memory?

I have a hypothetical COM object with the following signature

void MemAlloc(ref double[] test, int membercount)

where the memory is allocated in C++ using new/malloc. Once this is in C#, using the RCW, how do I ensure that the memory is freed correctly? I would think it would be difficult for .NET to free, considering in C++ you need to know if it was allocated with new/malloc/mm_malloc before you can correctly free it. So, what is the appopriate way to cleanup my C++ allocated array? Thanks.

From stackoverflow
  • Wrap it in an object that implements IDisposable and make sure that the C# wrapper gets disposed.

    Here's a blog I wrote about an easy way to implement IDisposable.

    Bradley Grainger : Using IDisposable is a good resource management strategy, but how do you implement the Dispose method to actually free the memory?
  • I believe you should use CoTaskMemAlloc() for memory that you want to explicitly free from the managed side. The CLR will take care of freeing the memory once it's no longer reachable. If you want to free it explicitly you can use the managed Marshal.CoTaskFree() routine.

    In general the interop marshaler and CLR abide by COM conventions for freeing memory; the recipient is responsible for freeing memory. So the CLR/Interop marshaler will usually take care of freeing memory that was allocated in a native call if that memory is returned to the managed caller.

    From Memory Management with the Interop Marshaler (msdn):

    The interop marshaler always attempts to free memory allocated by unmanaged code. This behavior complies with COM memory management rules, but differs from the rules that govern native C++.

    Confusion can arise if you anticipate native C++ behavior (no memory freeing) when using platform invoke, which automatically frees memory for pointers. For example, calling the following unmanaged method from a C++ DLL does not automatically free any memory.

    The runtime always uses the CoTaskMemFree method to free memory. If the memory you are working with was not allocated with the CoTaskMemAlloc method, you must use an IntPtr and free the memory manually using the appropriate method.

    Steve : thanks, that was exactly what I was looking for
  • I'm almost 100% sure the CLR will not automatically free the memory allocated for test (if it were PInvoke I would be 100% sure). The reason being, how does the CLR know what you used to allocate the memory in the first place? Namely it doesn't.

    A safer way to write this function is as follows

    void MemAlloc(ref IntPtr arrayPtr, int membercount)
    

    Once you get the call back you can do the following.

    var count = GetTheCount();
    var arrayPtr = IntPtr.Zero;
    obj.MemAlloc(ref arrayPtr, count);
    byte[] test = MarshalThePtrToByteArray(arrayPtr, count);
    Marshal.FreeCoTaskMem(arrayPtr);
    

    Here is a quick and dirty implementation of MarashalThePtrToByteArray

    byte[] MarashalThePtrToByteArray(IntPtr ptr, int count) {
      byte[] arr = new byte[count];
      for ( int i = 0; i < count; i++ ) {
        arr[i] = (byte)Marshal.PtrToStructure(ptr, typeof(byte));
        ptr = new IntPtr(IntPtr.ToInt64() + Marshal.SizeOf(typeof(byte)));
      }
      return arr;
    }
    
    Arnshea : For COM Interop the CLR/Interop marshaler uses the COM convention of releasing memory returned to the caller as long as the memory was allocated with CoTaskMemAlloc().
    JaredPar : @Arnshea, but how would the CLR know it was allocated with CoTaskMemAlloc? It cannot and many COM implementations do not use CoTaskMemAlloc for data.
    Arnshea : True. In this cases, where he's implementing the native method, he should use CoTaskMemAlloc() instead of new/mallow(). Then the interop marshaler will take care of freeing the memory.
    JaredPar : @Arnshea, I disagree. The CLR cannot know that he did or did not use CoTaskMemAlloc. There is just simply no way to reliably tell, therefore I believe the CLR cannot reliably make a free / no free call.
    Arnshea : The interop marshaler will call CoTaskMemFree() on the parameter (see the msdn article I linked in my answer).
    Steve : I feel that JaredPar is correct, which is the original thrust of my question. Perhaps it frees it in a try/catch block when it goes out of scope. If I allocated with CoTaskMemAlloc I'm OK. Otherwise I would leak.
    JaredPar : @Steve, @Arnshea, my solution will work but I'm still wondering about the auto-free. Arnshea is correct the CLR will free memory via CoTaskMem if it considers it allocated but I'm not sure how it makes that determination. Unfortunately my Adam Nathan books are in a box so I can't reference them
    JaredPar : (cont) I'm going to poke around a bit and see if I can find those books tonight.
  • The book .NET 2.0 Interoperbility Recipes looks to be handy. It seems to agree with what Arnshea said about CoTaskMemFree.

0 comments:

Post a Comment