0

I have codes interop between C# (core) and C++ (unmanaged DLL).

Memory allocated in C# using Marshal.AllocHGlobal() needs to be released in C# using Marshal.FreeHGlobal().

Memory allocated in C++ using new needs to be released in C++ using delete.

Can I just delete or FreeHGlobal() whenever I wish, since the GC is no longer tracing these memory handler?

3
  • No, they track memory allocations separately. One does not know about the other. Commented Jan 25, 2020 at 2:38
  • I just found out that in dotnet core 3.0, I can actually pass the C# byte[] directly to C++ without using Marshal.AllocHGlobal(). Commented Jan 25, 2020 at 4:59
  • @sk that is nothing new, .NET has always been able to do that. Read up on how PInvoke and marshaling actually work. Commented Jan 25, 2020 at 5:45

1 Answer 1

5

No, you cannot just use whatever method you want to free memory. You MUST use whatever the allocator requires you to use. Only the memory manager that allocates a given block of memory knows how to free that block of memory properly.

For instance, the documentation for Marshal.AllocHGlobal() states:

This method exposes the Win32 LocalAlloc function from Kernel32.dll.

When AllocHGlobal calls LocalAlloc, it passes a LMEM_FIXED flag, which causes the allocated memory to be locked in place. Also, the allocated memory is not zero-filled.

And the documentation for LocalAlloc() states:

To free the memory, use the LocalFree function. It is not safe to free memory allocated with LocalAlloc using GlobalFree.

Which is what Marshal.FreeHGlobal() uses:

FreeHGlobal exposes the LocalFree function from Kernel32.DLL, which frees all bytes so that you can no longer use the memory pointed to by hglobal.

So, it is allowed for C# code to allocate memory using Marshal.AllocHGlobal() and then for C++ code to free that memory using LocalFree(). And conversely, for C++ code to allocate memory using LocalAlloc(LMEM_FIXED) and then for C# code to free that memory using Marshal.FreeHGlobal().

Likewise, the Marshal class also has a Marshal.AllocCoTaskMem() method:

This method exposes the COM CoTaskMemAlloc function, which is referred to as the COM task memory allocator.

Memory allocated by CoTaskMemAlloc() is freed with CoTaskMemFree():

Frees a block of task memory previously allocated through a call to the CoTaskMemAlloc or CoTaskMemRealloc function.

Which is what Marshal.FreeCoTaskMem() uses :

FreeCoTaskMem exposes the COM CoTaskMemFree function, which frees all bytes so that you can no longer use the memory that the ptr parameter points to.

So, it is allowed for C# code to allocate memory using Marshal.AllocCoTaskMem() and then for C++ code to free that memory using CoTaskMemFree(). And conversely, for C++ code to allocate memory using CoTaskMemAlloc() and then for C# code to free that memory using Marshal.FreeCoTaskMem().

Now, that being said, the memory manager that C++ uses for its new and delete operators is implementation-defined. There is no guarantee (or likelihood) that new uses LocalAlloc() or CoTaskMemAlloc(), or that delete uses LocalFree() or CoTaskMemFree().

So, it is not legal for C# to free any memory allocated with new, or for C++ to delete any memory allocated by C#.

Sign up to request clarification or add additional context in comments.

2 Comments

Thanks for the detail info, that is very helpful. I do aware that C++ new / delete will actually call the constructor / destructor, which in turns will new / delete more memory. I am referring to basic memory pointer here.
@sk it is more complicated than that. Even with basic data types, you still can't mismatch allocations and deallocations. There are other factors at play that I didn't describe.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.