前言
記憶體空間是程式的靈魂,沒有了它,這個程式隻能是一堆廢代碼。本篇來以.Net8的JIT第一個加載的C#函數StelemRef(它在System.Private.CoreLib.dll)為例,看下.Net8 PreView3裡面是如何配置設定記憶體空間的大小的。
概括
1.配置設定要素
StelemRef的C#原型(為了便于閱讀,代碼經過提煉):
[DebuggerHidden] [StackTraceHidden] [DebuggerStepThrough] [MethodImpl(MethodImplOptions.AggressiveOptimization)] private unsafe static void StelemRef(Array array, IntPtr index, object obj) { ref object ptr = ref Unsafe.As<CastHelpers.ArrayElement[]>(array)[(int)(checked((IntPtr)(unchecked((long)index))))].Value; void* elementType = RuntimeHelpers.GetMethodTable(array)->ElementType; if (obj == ) {//省略} if (elementType != (void*)RuntimeHelpers.GetMethodTable(obj) && !(array.GetType() == typeof(object[]))) {//省略} CastHelpers.WriteBarrier(ref ptr, obj); }
它的IL代碼(為了便于閱讀,代碼經過提煉):
.method private hidebysig static void StelemRef(class System.Array 'array',native int index,object obj) cil managed { .locals (object& V_0, void* V_1, bool V_2, bool V_3, bool V_4) //為了避免過多代碼幹擾,此處上下省略一萬行 IL_0002: call !!0 IL_0016: call valuetype IL_002d: call valuetype IL_0040: call void IL_0050: callvirt instance class System.Type IL_005a: call class System.Type IL_005f: call bool System.Type::op_Equality(class IL_006f: call void IL_0074: nop IL_0075: ret } // end of method CastHelpers::StelemRef
可以看到這個StelemRef函數的IL代碼有三個參數,五個本地變量(.locals)。以及8個call的調用。這些都是配置設定的記憶體空間的必要元素。
2.配置設定的方式
一.參數
首先這個三個參數,array數組類型,index整型,obj的object類型,它們在JIT裡面不參與記憶體空間的計算,是以被排除了。
二.本地變量
五個本地變量,object& V_0是個object類型,占8個位元組,V_1是void* 類型占8位元組,V_2是bool占四個位元組,V_3是bool占四個位元組,V_4是bool占四個位元組。那麼本地變量的占據的空間是8+8+4+4+4=0x1C(28)個位元組。
三.call
再來看下8個call占用的空間,第一個call和最後一個call分别配置設定了4位元組的空間2*4==8,中間的六個call每個8位元組空間6*8==48,那麼8個call總共占據的空間是56個位元組。
四.預留白間
在第一個call和第二個call之間預留了8位元組,在最後一個call(也就是第8個call)後面預留了44個位元組。
五.畫像
它的整體畫像如下所示:
28 (0-7索引,三個參數及五個本地變量的空間) + 4 (9索引,因為前面三個參數和五個本地變量占據了8(0-7)個索引,而第八個索引為空,是以此處是9,(第一個call占據的空間)) + 8 (無索引,第一個call和第二個call中間的預留白間) + 48 (a-f索引 第二個call和第七個call占據的空間,總共六個call,每個八位元組) + 4 (0x10 index(索引) 最後一個call占據的空間) + (4 +32 +8) (這後面的全都是預留的空間)
那麼28+4+8+48+4+4+32+8=136(0x88個位元組)
3.觀察機器碼的配置設定
000002255E3020B8 55 push rbp 000002255E3020B9 57 push rdi 000002255E3020BA 48 81 EC 88 00 00 00 sub rsp,88h 000002255E3020C1 48 8D AC 24 90 00 00 00 lea rbp,[rsp+90h] 000002255E3020C9 C5 D8 57 E4 vxorps xmm4,xmm4,xmm4 000002255E3020CD C5 F9 7F 65 A0 vmovdqa xmmword ptr [rbp-60h],xmm4
以上是StelemRef函數的機器頭部代碼,第三行 sub rsp,88h。這個88h就是上面計算的記憶體空間0x88,它進入的時候給StelemRef函數配置設定記憶體的棧空間的大小0x88。
結尾
作者:江湖評談