What are Pinned objects and Pinned Object Heaps in C#?
Many wish to access the memory location if they have unmanaged code that references to a C# object they've built.
Now they can accomplish this with confidence if they pin the object.
But what exactly is pinning an object?
In simple words, a pinned object is nothing but an object that has a specific memory location.
Want to know more about Pinned Objects, then read on…
Table of contents
- What are pinned objects in C#?
- What are Pinned Object Heaps in C#?
- Why were pinned object heaps added to .NET5?
- What is happening in .NET6 with pinned object heaps?
What are pinned objects in C#?
As the name suggests, it is an object that has been pinned and cannot be moved. Normally, the Garbage Collector (GC) compacts memory by moving all objects to one or more clusters. This is done in order to free up enormous amounts of space.
This simply means that if someone else (from the outside) has a pointer to an object's memory address, it may lead to arbitrary data if the item has relocated.
When you pin an object, you're telling the GC not to move it. This is usually pointless and only makes sense when working with pointers, such as while using PInvoke. When you need to turn an address into a structure (in ‘memory layout’ terms), you must pin it if it is implemented in a class.
What are Pinned Object Heaps in C#?
A dedicated Pinned Object Memory, a fairly new type of managed heap segment, was added to the GC in .NET 5. (we have Small and Large Object Heaps so far). Pinning comes with its own set of drawbacks, as it causes fragmentation, and complicates object compaction a lot.
Depending on how much you pin and for how long, the resulting arrangement of the pinned items in memory, and a variety of other intermittent factors, the rules will result in some fragmentation.
So, in the end, it would be ideal to just remove pinned objects and relocate them to a location other than Small Object Heaps/Large Object Heaps. When considering heap compaction, the GC design will simply disregard this different location. As a result, we will obtain pinning behavior out of the box.
However, while the principle is obvious, implementing it in C# is not the case. Pinning is now a "two-phase process":
- You can create an object and save the reference to it somewhere.
- You can use a fixed keyword or a GCHandle to pin the item.
To put it another way, the allocator is unaware that the object being constructed will be pinned in the future.
As a result, with the introduction of the Pinned Object Heap in.NET 5, a new allocation API has been offered (for the first time since.NET 1.0). We can allocate arrays without using the new operator by utilizing one of the two methods:
Why were pinned object heaps added to .NET5?
Pinning was (and still is) considered an uncommon circumstance because it plainly interferes with GC's ability to compact the heap. And, as long as you can get your hands on it, you can pin any existing object with blittable fields. This indicates that you can pin something in any generation - young or old.
The best-case scenario is when you pin something for a short period of time that the GC doesn't notice. If there isn't a GC, objects aren't moved around. Therefore whether something is pinned or not has no effect. And, of course, maintaining control can be tough at times.
Alternatively, you can pin objects that aren't going to move. If you allocate several static objects when the process first starts up, they will all persist until the conclusion of the process lifetime. They will not move even if we use a compacting GC on them because they are already packed together. As a result, pinning or not pinning will have no effect.
What is happening in .NET6 with pinned object heaps?
Massive performance tweaking is being conducted on pinned object heap in .NET 6. The tuning has been maintained the same as it was in LOH so far, but it is not expected that POH will be utilized nearly as often as LOH.
Pinned object heap is only used for the small number of objects that are used for interoping because it is generally found in libraries, such as those in network IO. So a Pinned object heap should be modest in general; instead of "extending out" the heap, these items are now all allocated to their own small area.
We are not implying that you should convert all your pinned handles to be allocated on the Pinned object heap; if you only need to pin anything for a short period of time, it's best to leave it in gen0 so it can be reclaimed fast. Of course, you can find yourself in a situation where you can't use the pinned object heap because you don't have control over the object's allocation.
Let us wrap things up with some precautions that need to be taken while implementing the above concept -
- When pinning (particularly large) objects, try to release the pinned GcHandle as soon as possible, as this causes heap defragmentation to be interrupted.
- Nothing will help you if you forget to free GcHandle. Do it in a part of safe code (such as finally)