Patrick Dussud and I were talking recently about why the CLR supports pinned object… here are some thoughts from that discussion….

 

In order to pass data buffers between unmanaged and managed code, we need to fix the address of the buffer while it is being accessed by unmanaged code. We could have provided a special allocator for this purpose but that would have required the programmer to be aware of the fact that a buffer can be passed to an unmanaged piece of code right from the start. Instead we provided a pinning mechanism, where any managed buffer can be temporarily pinned or made unmovable by the GC. This is done implicitly by the manage-to-unmanaged layer (Pinvoke and COM) for arguments of certain classes such as String. This can also be done by creating a GCHandle passing the type  GCHandleType.Pinned and the buffer as arguments; the buffer will stay pinned until the GCHandle is freed

 

Performance model for pinned objects

While pinning is a flexible model that avoids data copies from managed buffer to unmanaged buffers, it does have some consequences on the performance of the managed program. Since GC isn’t able to move pinned objects, the GC heap can get fragmented; which means that the amount of memory allocated to the GC heap can be more than the data being used by the program. The GC will create some free blocks around the pinned objects that get in the way of compaction and if there are more of them than opportunities to reuse them, memory usage becomes higher than necessary. The GC has special policies to fight excessive fragmentation, but it comes at the expense of spending more time in the garbage collection on average. It is the old space/time tradeoff. Can the programmer do something about it? Yes, because old, stable objects don’t move very much because they are surrounded by other old stable objects so they can be pinned without creating much fragmentation. New objects, allocated amongst a slew of temporary objects are the worse if they are pinned for long period of time (such as a socket IO buffer, which has a usually long latency). They are in the middle of the most compacting area of memory and they stand in the way. The solution is for the IO buffers for long latency operations to be allocated from and returned to a pool of stable buffers, instead of being created and collected. This can make a big difference on server applications where a lot of requests are executed simultaneously and most of them wait on a long latency IO call.