前言:
今天大家对“netnew数组”都比较重视,同学们都想要剖析一些“netnew数组”的相关内容。那么小编在网上汇集了一些关于“netnew数组””的相关内容,希望姐妹们能喜欢,朋友们一起来学习一下吧!起因
在.Net 5中,在GC的公开方法中,增加了几个新的API.这几个API可能在工作用不到.这里也是学习一下.
AllocateArray 在托管堆根据长度分配数组,对数组对象分配默认值AllocateUninitializedArray 在托管堆根据长度分配数组,不对对象的属性分配默认值GetAllocatedBytesForCurrentThread 线程的生存期内在托管堆上分配的总字节数
1. 在项目中经常使用的方式,分配数组
public void ArrayTest(){ People[] peoples = new People[16]; Console.WriteLine(peoples.Length);}2. 在托管堆分配数组AllocateArray
public void AllocateArrayTest(){ //从.Net 5开始支持 //AllocateArray 在托管堆根据长度分配数组,对数组对象分配默认值 //第一个参数是需要分配数组的长度 //第二个参数是否在GC中固定 People[] arr = GC.AllocateArray<People>(16); Console.WriteLine(arr.Length);}
AllocateArray源码在src/coreclr/System.Private.CoreLib/src/System/GC.cs
public static T[] AllocateArray<T>(int length, bool pinned = false) // T[] rather than T?[] to match `new T[length]` behavior{ GC_ALLOC_FLAGS flags = GC_ALLOC_FLAGS.GC_ALLOC_NO_FLAGS; if (pinned) { if (RuntimeHelpers.IsReferenceOrContainsReferences<T>()) ThrowHelper.ThrowInvalidTypeWithPointersNotSupported(typeof(T)); flags = GC_ALLOC_FLAGS.GC_ALLOC_PINNED_OBJECT_HEAP; } return Unsafe.As<T[]>(AllocateNewArray(typeof(T[]).TypeHandle.Value, length, flags));}
public static T[] AllocateArray<T>(int length, bool pinned = false) // T[] rather than T?[] to match `new T[length]` behavior{ GC_ALLOC_FLAGS flags = GC_ALLOC_FLAGS.GC_ALLOC_NO_FLAGS; if (pinned) { if (RuntimeHelpers.IsReferenceOrContainsReferences<T>()) ThrowHelper.ThrowInvalidTypeWithPointersNotSupported(typeof(T)); flags = GC_ALLOC_FLAGS.GC_ALLOC_PINNED_OBJECT_HEAP; //是否需要在托管堆上固定 } //内部使用AllocateNewArray,这个API并不是c#实现 return Unsafe.As<T[]>(AllocateNewArray(typeof(T[]).TypeHandle.Value, length, flags));}
//MethodImplOptions表明AllocateNewArray是在CLR内部实现[MethodImpl(MethodImplOptions.InternalCall)]internal static extern Array AllocateNewArray(IntPtr typeHandle, int length, GC_ALLOC_FLAGS flags);
AllocateArray内部调用AllocateNewArray,具体实现是在CLR中实现.后面我们去跟AllocateNewArray源码实现.
3. AllocateUninitializedArray在托管堆上分配数组
public void AllocateUninitializedArrayTest(){ //从.Net 5开始支持 //AllocateUninitializedArray 在托管堆根据长度分配数组,不对对象的属性分配默认值 //第一个参数是需要分配数组的长度 //第二个参数是否在GC中固定 People[] arr = GC.AllocateUninitializedArray<People>(16); Console.WriteLine(arr.Length);}
AllocateUninitializedArray源码在src/coreclr/System.Private.CoreLib/src/System/GC.cs
[MethodImpl(MethodImplOptions.AggressiveInlining)] // forced to ensure no perf drop for small memory buffers (hot path)public static T[] AllocateUninitializedArray<T>(int length, bool pinned = false) // T[] rather than T?[] to match `new T[length]` behavior{ if (!pinned) //不固定在堆上固定对象 { if (RuntimeHelpers.IsReferenceOrContainsReferences<T>()) //如果是引用类型,直接根据类型使用直接用分组的方式 { return new T[length]; } // for debug builds we always want to call AllocateNewArray to detect AllocateNewArray bugs#if !DEBUG // small arrays are allocated using `new[]` as that is generally faster. if (length < 2048 / Unsafe.SizeOf<T>()) //数组长度较小的情况,也是使用直接的方式在堆上分配数组 { return new T[length]; }#endif } else if (RuntimeHelpers.IsReferenceOrContainsReferences<T>()) //如果该类型是引用类型并包含其他引用类型,会抛出异常 { ThrowHelper.ThrowInvalidTypeWithPointersNotSupported(typeof(T)); } // kept outside of the small arrays hot path to have inlining without big size growth return AllocateNewUninitializedArray(length, pinned); // remove the local function when is implemented static T[] AllocateNewUninitializedArray(int length, bool pinned) { GC_ALLOC_FLAGS flags = GC_ALLOC_FLAGS.GC_ALLOC_ZEROING_OPTIONAL; if (pinned) flags |= GC_ALLOC_FLAGS.GC_ALLOC_PINNED_OBJECT_HEAP; //重点还是AllocateNewArray,下面我们便去扒扒源码是如何实现的 return Unsafe.As<T[]>(AllocateNewArray(typeof(T[]).TypeHandle.Value, length, flags)); }}4. AllocateNewArray 在CLR是如何实现的
在.Net 看到MethodImplOptions.InternalCall标记的方法
在CoreCLR中一般可以先去src/coreclr/vm/ecalllist.h查找
在Mono中一般可以先去src/mono/mono/metadata/icall.c查找
在ecalllist.h文件中可以看到:
FCFuncElement("AllocateNewArray", GCInterface::AllocateNewArray)
根据GCInterface找不到对应cpp文件和头文件.如果用VS打开的话,可以根据解决方案中查找.
不过最后在src/coreclr/vm/comutilnative.cpp文件找到了.内部涉及的东西比较多.这里先不多源码进行解析,等后面有经验在来分析.
FCIMPL3(Object*, GCInterface::AllocateNewArray, void* arrayTypeHandle, INT32 length, INT32 flags){ CONTRACTL { FCALL_CHECK; } CONTRACTL_END; OBJECTREF pRet = NULL; TypeHandle arrayType = TypeHandle::FromPtr(arrayTypeHandle); HELPER_METHOD_FRAME_BEGIN_RET_0(); //Only the following flags are used by GC.cs, so we'll just assert it here. _ASSERTE((flags & ~(GC_ALLOC_ZEROING_OPTIONAL | GC_ALLOC_PINNED_OBJECT_HEAP)) == 0); pRet = AllocateSzArray(arrayType, length, (GC_ALLOC_FLAGS)flags); //这里 HELPER_METHOD_FRAME_END(); return OBJECTREFToObject(pRet);}FCIMPLEND
OBJECTREF AllocateSzArray(TypeHandle arrayType, INT32 cElements, GC_ALLOC_FLAGS flags){ CONTRACTL{ THROWS; GC_TRIGGERS; MODE_COOPERATIVE; // returns an objref without pinning it => cooperative } CONTRACTL_END; MethodTable* pArrayMT = arrayType.AsMethodTable(); return AllocateSzArray(pArrayMT, cElements, flags);}OBJECTREF AllocateSzArray(MethodTable* pArrayMT, INT32 cElements, GC_ALLOC_FLAGS flags){ CONTRACTL{ THROWS; GC_TRIGGERS; MODE_COOPERATIVE; // returns an objref without pinning it => cooperative } CONTRACTL_END; // IBC Log MethodTable access g_IBCLogger.LogMethodTableAccess(pArrayMT); SetTypeHandleOnThreadForAlloc(TypeHandle(pArrayMT)); _ASSERTE(pArrayMT->CheckInstanceActivated()); _ASSERTE(pArrayMT->GetInternalCorElementType() == ELEMENT_TYPE_SZARRAY); CorElementType elemType = pArrayMT->GetArrayElementType(); // Disallow the creation of void[] (an array of System.Void) if (elemType == ELEMENT_TYPE_VOID) COMPlusThrow(kArgumentException); if (cElements < 0) COMPlusThrow(kOverflowException); if ((SIZE_T)cElements > MaxArrayLength()) ThrowOutOfMemoryDimensionsExceeded(); // Allocate the space from the GC heap SIZE_T componentSize = pArrayMT->GetComponentSize();#ifdef TARGET_64BIT // POSITIVE_INT32 * UINT16 + SMALL_CONST // this cannot overflow on 64bit size_t totalSize = cElements * componentSize + pArrayMT->GetBaseSize();#else S_SIZE_T safeTotalSize = S_SIZE_T((DWORD)cElements) * S_SIZE_T((DWORD)componentSize) + S_SIZE_T((DWORD)pArrayMT->GetBaseSize()); if (safeTotalSize.IsOverflow()) ThrowOutOfMemoryDimensionsExceeded(); size_t totalSize = safeTotalSize.Value();#endif#ifdef FEATURE_DOUBLE_ALIGNMENT_HINT if ((elemType == ELEMENT_TYPE_R8) && ((DWORD)cElements >= g_pConfig->GetDoubleArrayToLargeObjectHeapThreshold())) { STRESS_LOG2(LF_GC, LL_INFO10, "Allocating double MD array of size %d and length %d to large object heap\n", totalSize, cElements); flags |= GC_ALLOC_LARGE_OBJECT_HEAP; }#endif if (totalSize >= g_pConfig->GetGCLOHThreshold()) flags |= GC_ALLOC_LARGE_OBJECT_HEAP; if (pArrayMT->ContainsPointers()) flags |= GC_ALLOC_CONTAINS_REF; ArrayBase* orArray = NULL; if (flags & GC_ALLOC_USER_OLD_HEAP) //如果还在原先堆上 { orArray = (ArrayBase*)Alloc(totalSize, flags); //在这里分配内存空间,Alloc内部是调用malloc orArray->SetMethodTableForUOHObject(pArrayMT); } else {#ifndef FEATURE_64BIT_ALIGNMENT if ((DATA_ALIGNMENT < sizeof(double)) && (elemType == ELEMENT_TYPE_R8) && (totalSize < g_pConfig->GetGCLOHThreshold() - MIN_OBJECT_SIZE)) { _ASSERTE(DATA_ALIGNMENT == sizeof(double) / 2); _ASSERTE((MIN_OBJECT_SIZE % sizeof(double)) == DATA_ALIGNMENT); // used to change alignment _ASSERTE(pArrayMT->GetComponentSize() == sizeof(double)); _ASSERTE(g_pObjectClass->GetBaseSize() == MIN_OBJECT_SIZE); _ASSERTE(totalSize < totalSize + MIN_OBJECT_SIZE); orArray = (ArrayBase*)Alloc(totalSize + MIN_OBJECT_SIZE, flags); //进行内存分配 Object* orDummyObject; if ((size_t)orArray % sizeof(double)) { orDummyObject = orArray; orArray = (ArrayBase*)((size_t)orArray + MIN_OBJECT_SIZE); } else { orDummyObject = (Object*)((size_t)orArray + totalSize); } _ASSERTE(((size_t)orArray % sizeof(double)) == 0); orDummyObject->SetMethodTable(g_pObjectClass); } else#endif // FEATURE_64BIT_ALIGNMENT {#ifdef FEATURE_64BIT_ALIGNMENT MethodTable* pElementMT = pArrayMT->GetArrayElementTypeHandle().GetMethodTable(); if (pElementMT->RequiresAlign8() && pElementMT->IsValueType()) { _ASSERTE(((pArrayMT->GetBaseSize() - SIZEOF_OBJHEADER) & 7) == 0); flags |= GC_ALLOC_ALIGN8; }#endif orArray = (ArrayBase*)Alloc(totalSize, flags); //进行内存分配 } orArray->SetMethodTable(pArrayMT); } // Initialize Object orArray->m_NumComponents = cElements; PublishObjectAndNotify(orArray, flags); return ObjectToOBJECTREF((Object*)orArray);}5. GetAllocatedBytesForCurrentThread 线程的生存期内在托管堆上分配的总字节数
public void GetAllocatedBytesForCurrentThreadTest(){ long currentThreadAllocateBytes = GC.GetAllocatedBytesForCurrentThread(); //线程的生存期内在托管堆上分配的总字节数 Console.WriteLine(currentThreadAllocateBytes);}
GetAllocatedBytesForCurrentThread源码也是在CoreCLR实现.在src/coreclr/vm/comutilnative.cpp
FCIMPL0(INT64, GCInterface::GetAllocatedBytesForCurrentThread){ FCALL_CONTRACT; INT64 currentAllocated = 0; Thread *pThread = GetThread(); //获取当前线程 gc_alloc_context* ac = pThread->GetAllocContext(); //获取当前线程的内存分配的上下文 currentAllocated = ac->alloc_bytes + ac->alloc_bytes_uoh - (ac->alloc_limit - ac->alloc_ptr); return currentAllocated;}FCIMPLEND
如果您觉得对您有用的话,可以点个赞或者加个关注,欢迎大家一起进行技术交流
标签: #netnew数组