500w 的引用类型和值类型到底有多大差异?
大家在寫代碼的時(shí)候,相信有很多朋友對 struct 認(rèn)知不是很足,導(dǎo)致能用 class 的地方絕對不用struct,但大家有沒有發(fā)現(xiàn),最近的幾個(gè) C# 版本中,底層框架中有很多 class 的替代品,比如說:
Task 和 ValueTask
Tuple 和 ValueTuple。
本質(zhì)上來說都是為了提少 GC 負(fù)擔(dān),提高程序性能。
今天就和大家簡單聊下,struct 和 class 到底在內(nèi)存占用上有多大差距,首先我們分別定義兩個(gè)空類型,然后分別灌入 500w 。
class?Program{static?void?Main(string[]?args){var?list?=?new?List<Test>(5000000);var?valueList?=?new?List<ValueTest>(5000000);for?(int?i?=?0;?i?<?5000000;?i++){list.Add(new?Test());valueList.Add(new?ValueTest());}Console.WriteLine("結(jié)束");Console.ReadLine();}}class?Test{}struct?ValueTest{}接下來用 windbg 看一下差異。
0:000>?!clrstack?-a OS?Thread?Id:?0x4040?(0)Child?SP???????????????IP?Call?Site 00000000001CE920?00007ffb8fb147bc?System.Console.ReadLine()?[/_/src/libraries/System.Console/src/System/Console.cs?@?629] 00000000001CE950?00007ffb2b4c621b?ConsoleApp6.Program.Main(System.String[])?[D:\net5\ConsoleApp1\ConsoleApp6\Program.cs?@?24]PARAMETERS:args?(0x00000000001CE9D0)?=?0x000000000281a650LOCALS:0x00000000001CE9B8?=?0x000000000281b6780x00000000001CE9B0?=?0x000000000281b6980x00000000001CE9AC?=?0x00000000004c4b400x00000000001CE9A0?=?0x00000000000000000x00000000001CE99C?=?0x00000000000000000:000>?!DumpObj?/d?000000000281b678 Name:????????System.Collections.Generic.List`1[[ConsoleApp6.Test,?ConsoleApp6]] MethodTable:?00007ffb2b594240 EEClass:?????00007ffb2b57f0b0 Size:????????32(0x20)?bytes File:????????C:\Program?Files\dotnet\shared\Microsoft.NETCore.App\5.0.13\System.Private.CoreLib.dll Fields:MT????Field???Offset?????????????????Type?VT?????Attr????????????Value?Name 00007ffb2b597638??4001d3c????????8?????System.__Canon[]??0?instance?0000000012811038?_items 00007ffb2b48b258??4001d3d???????10?????????System.Int32??1?instance??????????5000000?_size 00007ffb2b48b258??4001d3e???????14?????????System.Int32??1?instance??????????5000000?_version 00007ffb2b597638??4001d3f????????8?????System.__Canon[]??0???static?dynamic?statics?NYI?????????????????s_emptyArray 0:000>?!DumpObj?/d?000000000281b698 Name:????????System.Collections.Generic.List`1[[ConsoleApp6.ValueTest,?ConsoleApp6]] MethodTable:?00007ffb2b594de8 EEClass:?????00007ffb2b5a5ea0 Size:????????32(0x20)?bytes File:????????C:\Program?Files\dotnet\shared\Microsoft.NETCore.App\5.0.13\System.Private.CoreLib.dll Fields:MT????Field???Offset?????????????????Type?VT?????Attr????????????Value?Name 00007ffb2b596c60??4001d3c????????8?...eApp6.ValueTest[]??0?instance?0000000014e36a70?_items 00007ffb2b48b258??4001d3d???????10?????????System.Int32??1?instance??????????5000000?_size 00007ffb2b48b258??4001d3e???????14?????????System.Int32??1?instance??????????5000000?_version 00007ffb2b596c60??4001d3f????????8?...eApp6.ValueTest[]??0???static?dynamic?statics?NYI?????????????????s_emptyArray 0:000>?!objsize?000000000281b678 sizeof(000000000281B678)?=?160000056?(0x9896838)?bytes?(System.Collections.Generic.List`1[[ConsoleApp6.Test,?ConsoleApp6]]) 0:000>?!objsize?000000000281b698 sizeof(000000000281B698)?=?5000056?(0x4c4b78)?bytes?(System.Collections.Generic.List`1[[ConsoleApp6.ValueTest,?ConsoleApp6]])從輸出中可以看到,list=160M,而 valuelist=5M 居然相差 32 倍, 這種量級的差異,在高性能的場景下足以讓我們充分考量了,對吧!
我相信有很多朋友應(yīng)該能搞明白為什么會(huì)是 32 倍。真有不明白的同學(xué),我再來分析一波吧。
先看struct,用 dp 0000000014e36a70 看內(nèi)存地址。
0:000>?!da?0000000014e36a70 Name:????????ConsoleApp6.ValueTest[] MethodTable:?00007ffb2b596c60 EEClass:?????00007ffb2b596be0 Size:????????5000024(0x4c4b58)?bytes Array:???????Rank?1,?Number?of?elements?5000000,?Type?VALUETYPE Element?Methodtable:?00007ffb2b594760 [0]?0000000014e36a80 [1]?0000000014e36a81 [2]?0000000014e36a82 [3]?0000000014e36a83 [4]?0000000014e36a84 [5]?0000000014e36a85 [6]?0000000014e36a86 [7]?0000000014e36a87 [8]?0000000014e36a88 [9]?0000000014e36a89 [10]?0000000014e36a8a [11]?0000000014e36a8b [12]?0000000014e36a8c [13]?0000000014e36a8d [14]?0000000014e36a8e [15]?0000000014e36a8f [16]?0000000014e36a90 ...0:000>?dp?0000000014e36a70 00000000`14e36a70??00007ffb`2b596c60?00000000`004c4b40 00000000`14e36a80??00000000`00000000?00000000`00000000 00000000`14e36a90??00000000`00000000?00000000`00000000 00000000`14e36aa0??00000000`00000000?00000000`00000000 00000000`14e36ab0??00000000`00000000?00000000`00000000 00000000`14e36ac0??00000000`00000000?00000000`00000000 00000000`14e36ad0??00000000`00000000?00000000`00000000 00000000`14e36ae0??00000000`00000000?00000000`00000000從輸出看,對于一個(gè)空 struct 而言在內(nèi)存中只占用了 1byte。
接下來看一下 引用類型,用 dp 0000000012811038 即可。
0:000>?dp?0000000012811038 00000000`12811038??00007ffb`2b596a80?00000000`004c4b40 00000000`12811048??00000000`028110e8?00000000`02811100 00000000`12811058??00000000`02811118?00000000`02811130 00000000`12811068??00000000`02811148?00000000`02811160 00000000`12811078??00000000`02811178?00000000`02812500 00000000`12811088??00000000`028128a8?00000000`028128c0 00000000`12811098??00000000`028128d8?00000000`028128f0 00000000`128110a8??00000000`02812908?00000000`028129e8剛才也提到了兩者相差32倍,也就是一個(gè)引用類型應(yīng)該要占用 32byte才對,是吧,那這個(gè)是怎么算的呢?首先在 64bit 平臺(tái)引用類型的最小size=3*8=24byte, 也即 **(對象頭+方法表指針+空占位符)**, 這個(gè) size 在 coreclr 中也是有 const 聲明的, 剩下的 8byte 就是上面用 dp 命令看到的數(shù)組中的每一元素的 方法表指針 啦。
至此,大家都明白了吧。
總結(jié)
以上是生活随笔為你收集整理的500w 的引用类型和值类型到底有多大差异?的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: .NET Core剪裁器升级瘦身引擎,并
- 下一篇: Blazor University (4