Click to See Complete Forum and Search --> : Grr, garbage collector, don't move my arrays!
BlueEyedCat
January 25th, 2008, 11:13 AM
Hi;
I need a memory block allocated on heap (on a fixed posisiton) that GC can't move. I need this because unmanaged code will read data from this memory block on a multi-threaded manner (so "fixed" statement will not work since threads in unmanaged code may still be manipulating this memory though code flow fell out of fixed scope).
Is this possible with C#? Or do you recommend using nasty GlobalAlloc api to allocate memory on heap manually from C#?
Thanks all...
STLDude
January 25th, 2008, 11:41 AM
Look it up pin_ptr, MSDN has info.
BlueEyedCat
January 25th, 2008, 12:50 PM
Thanks for reply but AFAIK, pin_ptr is specific for managed C++ (or am i wrong?). I need a solution for C#. Thanks anyway...
MadHatter
January 25th, 2008, 01:20 PM
GCHandle objHandle = GCHandle.Alloc(array);
// do whatever
objHandle.Free();
if you have an array of value types you can also pin the array:
GCHandle objHandle = GCHandle.Alloc(array, GCHandleType.Pinned);
if they're reference types you cant pin it, but that will keep GC from messing w/ it.
STLDude
January 25th, 2008, 01:22 PM
What about:
IntPtr addressOfMyBuffer = Marshal.AllocHGlobal(yourArrayHere);
TheCPUWizard
January 25th, 2008, 01:22 PM
Oh if only people would read the documentation....
fixed statement (http://msdn2.microsoft.com/en-us/library/f58wzh21(VS.80).aspx)
riscutiavlad
January 25th, 2008, 01:26 PM
.NET does has a way of handling this. Use InteropServices and create a GCHandle for your buffer (array).
using System.Runtime.InteropServices;
...
GCHandle pinnedBuffer = GCHandle.Alloc(buffer, GCHandleType.Pinned);
The Pinned GCHandleType tells the garbage collector to pin the array into place so, even when compacting memory, it won't move it. To obtain an IntPtr for that address use
IntPtr ptrToPinned = pinnedBuffer.AddrOfPinnedObject();
Now you can pass the ptrToPinned to any unmanaged method. When you are done with it, you can let the garbage collecter now by calling
pinnedBuffer.Free();
I suggest you place the code in a try - finally block to make sure the memory is freed even if an exception occurs.
MadHatter
January 25th, 2008, 01:26 PM
you cannot pin or obtain the pointer to or get the size of reference types.
GCHandle is what you need. you can pin it if its members are value types, otherwise you cant, but allocating the handle will keep you from having it moved around.
Mutant_Fruit
January 25th, 2008, 01:35 PM
Oh if only people would read the documentation....
fixed statement (http://msdn2.microsoft.com/en-us/library/f58wzh21(VS.80).aspx)
It won't work. He explicitly said he needs the object to remain pinned longer than the call into unmanaged land lasts. The array will be filled asynchronously.
The GCHandle class should do it alright.
DeepT
January 25th, 2008, 01:43 PM
Out of curiosity, what kind of C# application needs a block of memory in a fixed location? Even when considering IPC, you share memory blocks via system objects, not actual memory addresses.
TheCPUWizard
January 25th, 2008, 01:48 PM
You can still use the fixed statement (I do it all the time). you simply implement your architecture so that everything occurs as external method to the fixed statement.
GCHandle() will work, but can be difficult to properly handle under all exception conditions.
Also it is generally a really bad idea to pin memory for more than a very short time. Because of the way that the GC works internally, an existing pin effectively prevents GC of that generation entirely! :eek:
riscutiavlad
January 25th, 2008, 02:07 PM
Out of curiosity, what kind of C# application needs a block of memory in a fixed location? Even when considering IPC, you share memory blocks via system objects, not actual memory addresses.
A C# application that needs to call a native Windows function. If that function needs a buffer to read from, if you don't pin the memory, the garbage collector might move it and the native function trying to read it will fail.
A fixed statment works but I, personally, prefer not using unsafe code. Using a GCHandle is the managed way of doing it :)
TheCPUWizard
January 25th, 2008, 02:18 PM
A C# application that needs to call a native Windows function. If that function needs a buffer to read from, if you don't pin the memory, the garbage collector might move it and the native function trying to read it will fail.
A fixed statment works but I, personally, prefer not using unsafe code. Using a GCHandle is the managed way of doing it :)
I dont see how you equate the fixed statement with unsafe code.. :confused: :confused:
If you look at the fixed statement generated code, it actually creates a GCHandle and releases it inside a finally block so it is perfectly exception safe.
It is very similar to the lock statement in that it respect...
riscutiavlad
January 25th, 2008, 02:24 PM
Well, for starters, because using the fixed statement requires an unsafe context.
Mutant_Fruit
January 25th, 2008, 07:12 PM
Also it is generally a really bad idea to pin memory for more than a very short time. Because of the way that the GC works internally, an existing pin effectively prevents GC of that generation entirely! :eek:
Really? Is that documented everywhere? It seems like a fairly serious limitation to have in a garbage collected language, especially one which prides itself on interop with unmanaged code.
If what you say is true, then all it would take to stop objects being collected would be a single pinned object in each generation. That's something which is pretty simple to do.
TheCPUWizard
January 25th, 2008, 08:40 PM
Really? Is that documented everywhere? It seems like a fairly serious limitation to have in a garbage collected language, especially one which prides itself on interop with unmanaged code.
If what you say is true, then all it would take to stop objects being collected would be a single pinned object in each generation. That's something which is pretty simple to do.
IT depends on where the pinned memory is allocated. Just think about it for a second. The way GG (Gen0) works is that all "valid" objects are moved up to the GEN1 heap, and then the GEN0 pointer is reset to the base (so that the memory can be allocated in a linear manner for future allocations on the GEN0 heap.
Now if an object on the GEN0 heap is pinned, it can not be moved to the GEN1 heap. This means that the GEN0 pointer can not be reset back (because it grows) it would trash the pinned object.
The alternative would be to keep track of what areas have to be "protected", but that would incur MAJOR overhead. One of the advaqntages of the GC memory model is that allocations are incredibly fast, so this would be REAL BAD.
The good news (because if there was no good news, pinning would be nearly fatal) is that the system has two alternatives:
1) An un-pinned object object may be freely moved. So the compiler can actually move the object OFF the normal heaps prior to pinning it. This typically happens, but does involve overhead.
2) The object can be originally allocated OFF the heap, which eliminates the original problem as well as the copy issue, but does add ovhead at the time where the object is unpinned.
Version 1.x had some real issues with these conditions,and you could usually get significant improvements by doing all of the memory management on the unmanaged side.
Version 2.0 has addressed most of these to produce optimal results, and the impact is rarely noticable. However the algorythms are not perfect. To prove this you can create a large number of big (but not large object heap eligible) objects, pass them to a distant part of the code (eg in another assembly), then loop through them performing pins (either via fixed or GCHandles that are created and destroyed). Fortunately this is a rather parasitic case....
Getting back to my original statement about using fixed... This typically keeps things very localized where there will be few (if any) new allocations occuring within the fixed block. If there are no allocations, there will be no GC. This allows additional optimizations.
My experience has been that while problems caused by ANY of the points I have mentioned are EXTREMELY rare, when they occur they can be a real pain. Having small local "fixed" statements I have NEVER seen the problem, and I am 100% that I have not pinned something for longer than necessary.
Regarding the need for the unsafe context, I find that it is usually necessary to have an unsafe context to accomplish the task that originally required me to pin the memory in the first place.
If I am developing an application which has significant interaction with unmanaged code that requires this type of pinning (especially the condition where a buffer must remain at one location across multiple calls), then the odds are greater than 99.55% that I will write that part of the system in mixed mode C++.
MadHatter
January 25th, 2008, 09:27 PM
from what I've read, pinning doesn't keep the entire generation from being processed, only the pinned object, which results in fragmentation. If the entire gen was kept from being processed then fragmentation wouldn't be an issue.
Mutant_Fruit
January 26th, 2008, 05:55 AM
The only documentation pinned objects states that a pinned object means the GC can't compact the pinned memory. This can lead to fragmentation. Nowhere does it say that a pinned object 'effectively stops GC'ing from happening'.
When using the async methods BeginSend and BeginReceive in sockets, the byte[] buffer you pass into the method gets pinned (indefinitely) until the unmanaged code has finished filling the buffer. I've written socket-heavy code and i never had issues where my memory usage ballooned out the window despite the fact i had dozens of pinned objects in each generation.
humble_learner
July 7th, 2008, 04:34 AM
When you pass in a pointer to an array of datastructures created in the Managed Code to a DLL function in unmanaged code, what are the problems that are associated with traversing the array in the unmanaged code ?
Can you just move from one array index to another by just incrementing the pointer in the unmanaged code ?
codeguru.com
Copyright Internet.com Inc., All Rights Reserved.