Guide to using Pinning Pointers

Introduction

Having written an article on interior pointers, I thought the next logical topic for an article from me should be pinning pointers. This article will explain what pinning pointers are, when they come in handy and how they can be harmful to the overall performance of your .NET application. If you haven't already read my article on interior pointers and if you don't know what interior pointers are, then it'd be a good idea to quickly go through that article :-

Now that I've enjoyed the rather conceited satisfaction of having plugged my own article, lets get going.

What are pinning pointers?

During Garbage Collection cycles, the GC frequently compacts the CLI heap, relocates objects and adjusts managed references so that they continue referencing the same object that they were before the GC cycle. Since this is done transparently, the programmer needn't really be bothered with this at all unless there is an unmanaged pointer pointing to a CLI heap object. Since the GC cannot adjust unmanaged pointers, this would obviously result in havoc and so unmanaged pointers are not allowed to point to CLI objects. But there might be situations where you want to pass a CLI object to an unmanaged function (possibly during native interop) and to facilitate this, the CLR provides us with pinning pointers. A pinning pointer (or a pinned pointer) prevents the GC from relocating the pinned object for as long as the pinning pointer remains in scope. It's not difficult to write some arguably contorted code to verify this and we'll do just that :-

See below our test ref class, the DoLotsOfAllocs function that we use to fill up the CLI heap and to force a GC cycle, and a native function that takes an unmanaged int* as argument.

p1 (the pinned pointer) retains its value even after a GC cycle, while p2 (an interior pointer) has changed confirming that while the unpinned object has been relocated, the pinned object has remained fixed in the CLI heap.

Passing to native code

If all we needed to do was directly access the underlying object via a pointer and do pointer operations (like pointer arithmetic and comparison), then it'd wholly suffice to purely use interior pointers. But interior pointers do not convert to native pointers and thus cannot be passed to native functions expecting native pointers. That's where pinned pointers come in handy with their implicit conversion to native pointers.

PtrToStringChars returns a const interior pointer which we first const_cast to a volatile interior pointer which gets implicitly converted to a pinned pointer. Now we pass this pinned pointer to Test2(...) which accepts an unmanaged wchar_t* and the pinned pointer is implicitly converted to the unmanaged pointer.

Now that I've shown you how pinned pointers have advantages and uses over interior pointers, I've got to tell you this - only use pinned pointers when you have to and even when you have to, make sure that your pinned pointer does not remain in scope for too long. This is explained in the next section.

Pinning pointers and heap fragmentation

Lets assume that we have 3 objects O1, O2 and O3 in the CLI heap [assume Gen-0], which will now look like this :-

O1

O2

O3

Free Space

Now, assume that O2 is now an unreachable object and a GC occurs. After GC the CLI heap will look like this :-

O1

O3

Free Space

Now assume that O3 is a pinned object (means there are 1 or more pinning pointers pointing to it). If so, since the GC cannot relocate O3, the CLI heap will look like this :-

O1

O3

Free Space

Now imagine what happens if we had numerous such pinned objects (indicated by a gray background); the CLI heap would look something like :-

O1

O3

O5

O6

O7

O8

Free Space

Now, the heap is thoroughly fragmented, which makes GC cycles and memory allocation for new objects rather slower and less efficient than otherwise, and there is also the risk that there won't be a large enough contiguous block of free space to allocate a large object (say an array) which will result in some kind of memory exception.

Recommendations for using pinning pointers

[ Please note that these are strictly my ideas and could be remarkably erroneous for all I know, but with feedback from some of you out there who might know better, I hope to narrow down the level of inaccuracy. ]

Use pinning pointers only when absolutely necessary, else try and use interior pointers or tracking references.

When you do use pinning pointers, try and reduce the duration of their scope (the longer they remain in scope, the greater the risk of heap fragmentation).

If you need to pin multiple objects, try and allocate those objects together, so that the pinned objects will all be in one contiguous area in the CLI heap and the extent of fragmentation will be reduced.

During interop calls, first check and see if the marshalling layer does any pinning for you, and do your own pinning only if required.

Avoid situations where you pass a pinned pointer to a native pointer and risk using the native pointer after the pinned pointer has gone out of scope. Once the pinned pointer is out of scope, the object is no longer pinned and might get relocated or collected by the GC, in which case your native pointer is now pointing to a random area in the CLI heap.

Conclusion

I thoroughly enjoyed writing this article and am feeling rather proud of the fancy html-table graphics that I used for my CLI heap representations. If any of you feel that the graphics sucked, please don't let me know, so I can continue basking in an idiotic glory that makes sense only to me. On a more serious note, please feel free to submit you unmitigated feedback so that I can improve the article to the maximum extent possible. Thank you.

Share

About the Author

Nish Nishant is a Software Architect/Consultant based out of Columbus, Ohio. He has over 15 years of software industry experience in various roles including Lead Software Architect, Principal Software Engineer, and Product Manager. Nish is a recipient of the annual Microsoft Visual C++ MVP Award since 2002 (13 consecutive awards as of 2014).

Nish is an industry acknowledged expert in the Microsoft technology stack. He authored C++/CLI in Action for Manning Publications in 2005, and had previously co-authored Extending MFC Applications with the .NET Framework for Addison Wesley in 2003. In addition, he has over 140 published technology articles on CodeProject.com and another 250+ blog articles on his WordPress blog. Nish is vastly experienced in team management, mentoring teams, and directing all stages of software development.

Thanks for your quick reply. I am very new to C++ CLI. I have ref class as below in my C++ CLI wrapper and this will be passed to the native function like ProcessResults(results* r). I used Int_ptr and I have been told that I have pin the object before passing it to the native function. It seems like we can not pin the struct directly as its not a blittable type. Appreciate your help

How do you pass a managed pointer (i.e. long* addrPtr, which converts to an interior pointer.) to a native function expecting a void**. Also can you use IntPtr or UIntPtr to pass a managed type to a native function expecting a pointer.

The article is great and I think that is eaxactly what I need
to solve my problem. I have a byte array allocated on managed
memory. I need a pointer to that array so that I can pass it as
a parameter to a unmanaged function.

However I have a question regarding the syntax, I'm using a byte array
on managed code, but I need to supply a char * to the native fucntion.
Is the conversion simple, meaning something like this:

pin_ptr<<Byte>> aux = &bufferTemp[0];
unsigned char * auxPtr = aux;

Thanks in advance for your help.
Keep going with the good work.
Best regards

awesome articles you have here, I used to work in pure C++, now I am on C# but I came across an interop issue where I needed C++/CLI so now I'm learning (actually, you replied me on the newsgroups.. posted another question there so please check)