首先,我知道我甚至不应该尝试这样做,但我喜欢尝试没有意义的事情,我对this post很感兴趣,并根据我的需要进行了修改,我想通过在非托管内存中实现数组分配来改进它。
public static class Unmanaged<T> where T : class
{
private delegate T CreateHandler(IntPtr ptr);
private delegate IntPtr FindHandler(T obj);
private static readonly CreateHandler Create;
private static readonly FindHandler Find;
private static readonly IntPtr _typePointer;
private static readonly int _typeSize;
static Unmanaged()
{
Type type = typeof(T);
_typePointer = type.TypeHandle.Value;
_typeSize = Marshal.ReadInt32(_typePointer, sizeof(int));
DynamicMethod method = new DynamicMethod(nameof(Create), typeof(T), new[] { typeof(IntPtr) }, typeof(Unmanaged<T>), true);
var generator = method.GetILGenerator();
generator.Emit(OpCodes.Ldarg_0);
generator.Emit(OpCodes.Ret);
Create = (CreateHandler)method.CreateDelegate(typeof(CreateHandler));
method = new DynamicMethod(nameof(Find), typeof(IntPtr), new[] { typeof(T) }, typeof(Unmanaged<T>), true);
generator = method.GetILGenerator();
generator.Emit(OpCodes.Ldarg_0);
generator.Emit(OpCodes.Ret);
Find = (FindHandler)method.CreateDelegate(typeof(FindHandler));
}
public static T New()
{
IntPtr handle = Marshal.AllocHGlobal(_typeSize);
IntPtr pointer = handle + IntPtr.Size;
Marshal.WriteIntPtr(pointer, _typePointer);
return Create(pointer);
}
public static void Destroy(T obj)
{
IntPtr pointer = Find(obj);
IntPtr handle = pointer - IntPtr.Size;
Marshal.FreeHGlobal(handle);
}
}
public static class UnmanagedArray<T>
{
private static readonly Delegate Create;
private static readonly Delegate Find;
private static readonly IntPtr _typePointer;
private static readonly int _typeSize;
static UnmanagedArray()
{
Type type = typeof(T[]);
Type createType = Expression.GetFuncType(typeof(IntPtr), typeof(T[]));
Type findType = Expression.GetFuncType(typeof(T[]), typeof(IntPtr));
_typePointer = type.TypeHandle.Value;
_typeSize = Marshal.ReadInt32(_typePointer, sizeof(int));
DynamicMethod method = new DynamicMethod(nameof(Create), typeof(T[]), new[] { typeof(IntPtr) }, typeof(UnmanagedArray<T>), true);
ILGenerator generator = method.GetILGenerator();
generator.Emit(OpCodes.Ldarg_0);
generator.Emit(OpCodes.Ret);
Create = method.CreateDelegate(createType);
method = new DynamicMethod(nameof(Find), typeof(IntPtr), new[] { typeof(T[]) }, typeof(UnmanagedArray<T>), true);
generator = method.GetILGenerator();
generator.Emit(OpCodes.Ldarg_0);
generator.Emit(OpCodes.Ret);
Find = method.CreateDelegate(findType);
}
public static T[] New(int count)
{
Type elementType = typeof(T);
int elementSize = GetElementSize(elementType);
int size = _typeSize + elementSize * count;
IntPtr handle = Marshal.AllocHGlobal(size);
IntPtr pointer = handle + IntPtr.Size;
Marshal.WriteIntPtr(pointer, _typePointer);
InitializeArray(handle, size, count);
return (T[])Create.DynamicInvoke(pointer);
}
public static void Destroy(T[] obj)
{
IntPtr pointer = (IntPtr)Find.DynamicInvoke(obj);
IntPtr handle = pointer - IntPtr.Size;
Marshal.FreeHGlobal(handle);
}
private static int GetElementSize(Type type)
{
if (type.IsValueType) return Marshal.SizeOf(type);
else return IntPtr.Size;
}
private static unsafe void InitializeArray(IntPtr handle, int size, int count)
{
int startPosition = IntPtr.Size * 2;
int endPosition = IntPtr.Size * 2 + sizeof(int);
byte byteSize = 8;
byte* bytes = (byte*)handle;
for (int index = startPosition, positionIndex = 0; index < endPosition; index++, positionIndex++)
{
bytes[index] = (byte)(count >> positionIndex * byteSize);
}
for (int index = _typeSize; index < size; index++)
{
bytes[index] = 0;
}
}
}
对于值类型(short、int、long等),它可以工作(我还没有尝试过struct),但是它在为对象分配数组后崩溃,如下所示...
static void Main(string[] args)
{
Test test = Unmanaged<Test>.New();
Test[] array = UnmanagedArray<Test>.New(2);
test.Value = 5;
array[0] = test;
Console.WriteLine(array[0].Value); //AccessViolationException
Unmanaged<Test>.Destroy(test);
UnmanagedArray<Test>.Destroy(array);
}
不知道为什么它崩溃了AccessViolationException(你不能用正常的方法捕捉),最好的部分是它不总是发生。调试时检查堆栈,数组实际上保存了对非托管内存中对象的引用(即使检查地址,它们匹配),但后来它崩溃了(非常频繁)...
有什么建议吗?
- 更新**
原来在为reference Type分配非托管内存时,你不能赋值或读取嵌套的reference *。可能这与GC管理引用的方式有关(不确定,只是猜测)。这就是为什么在上面的链接中作者将reference封装在struct内部。这样你就在使用值类型,如果你改变数组来存储那些struct(包含你的非托管引用),它就会工作。
- 嵌套引用指的是类中的字段,您试图在非托管内存和数组元素中分配空间(对象数组本身是堆栈上对堆上实际数组的引用-如果它不是类中的字段-包含"嵌套"引用作为对象元素-也在堆上)
1条答案
按热度按时间w6mmgewl1#
GC将无法更新指向非托管内存中其他对象的指针,并且不将它们视为活引用,因此指向对象中托管内存的任何指针要么无效,要么死亡(两次无效)。正如Marc所说,您唯一可以放置在未Map内存中的是
struct
s,它遵循unmanaged
约束。