天天看點

.Net8的JIT是如何計算函數記憶體空間大小的?

作者:opendotnet

前言

記憶體空間是程式的靈魂,沒有了它,這個程式隻能是一堆廢代碼。本篇來以.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。

結尾

作者:江湖評談

繼續閱讀