In the .NET framework memory control is mostly autonomous and is controlled by the CLR. Although in managed languages we do not care much about controlling memory but when we have to interact with other languages we have to be aware of the memory implications. When we create an object in memory there is no guarantee that the object will remain in the same location it was created in. This is because the GC moves memory around by itself when needed to.
Variable movement in memory
I am sure that everyone reading this have already read about how GC collects memory and generations. As you know that GC allocates memory spaces in bulk. When needed GC collects unused memory and moves it. For example if the object you are using is collected, in order make better allocation GC can move it a different block of memory and free the block you were in. Let think we have a variable called "x" and it allocated in the memory like in the position like shown below.
Figure 1: Before GC collection
in the figure above 1 is the space that are allocated and 0 are the ones that need to garbage collected because there is no reference to it. When GC collects it might decide to move the occupied items to the top block keep the second block empty by compacting. If so our memory could look like this diagram below
As you see that GC has moved all items to the first block of memory and the second block is empty which will result in faster memory allocation for new variables and it makes sense to do to so. Our variable x has moved to a different location in memory space which means the address of the memory where the variable is kept has changed. (Please keep in mind that the address space used does not have anything is physical ram as the OS may decide to change the physical address anytime and the address we are referring to is virtual address. Usually GC collect after 256KB of memory has become occupied, depending on the version of GC and OS and the mode it runs. As GC is independent of the thread you are running, it can collect any time.
If you are doing unmanaged memory operation and you are using the memory address and GC can come in move the memory away as you use the address to do unmanaged operations.
Pin down: API Calls to PInvoke
So can can this be a problem when we are calling windows APIs? For example, what happens if the window handle (hwnd) that I specified in the pinvoke is moved to a different memory address. The variable is pinned down to memory before the PInvoke call and pinned status is released just after the call. This is done automatically by the CLR. So what would have happened if GC had come in while the unmanaged PInvoke was running? See the figure below
As you can see in the figure above that the variable x did not move to another virtual address in memory. This is useful when I need some unmanaged code to access a memory address that is constant. For example lets think of a scenario where I have an integer array which I pass to some unmanaged function and then I change the values of the variable at times and unmanaged function read changed values in the integer array and does some work. In such a scenario I will need the array to remain in one constant space. So I will need GCHandle class to pin it down in memory
We find the class in System.Runtime.InteropServices namespace. In order use this class we will need SecurityPermission with Unmanaged code flag most of the time. Each application domain has a for GC handles, with this GCHandle class we can control what items are in that table. The Garbage Collector actually reads this table and abides by it. Each entry in the table points to an object in the managed heap and how GC should treat it. There are four behaviors types as described in the GCHandleType enumeration. They are as follows,
Weak and WeakTrackResurrection for tracking weak referenced objects (see earlier post WeakReference: GC knows the Best for more on weak references). The Normal type is used to keep an object alive even if there are no reference to it. We are interested in the last type called Pinned. This tells the Garbage Collector to keep this object in memory even if there are no reference to it and never to move this object around in memory. See an example below on how to use the GCHandle class
string name = "My Name"; byte nameinbyte = ASCIIEncoding.ASCII.GetBytes(name); // Pin down the byte array GCHandle handle = GCHandle.Alloc(nameinbyte, GCHandleType.Pinned); IntPtr address = handle.AddrOfPinnedObject (); // Do stuff ... with the pinned object address // .... handle.Free();
Things to Remember
Please note that too many pinned object will make the GC slowdown. Also only blittable types and arrays of blittable types can be used. If you have written a custom object you can make it blittable by implenting a custom marshaler with the ICustomMarshaler interface.