成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

用 Span 對(duì) C# 進(jìn)程中三大內(nèi)存區(qū)域進(jìn)行統(tǒng)一訪問 ,太厲害了!

存儲(chǔ) 存儲(chǔ)軟件 后端
總的來說,這一篇主要是從思想上帶大家一起認(rèn)識(shí) Span,以及如何用 Span 對(duì)接 三大區(qū)域內(nèi)存,關(guān)于 Span 的好處以及源碼解析。

[[348727]]

 一:背景

1. 講故事

前段時(shí)間寫了幾篇 C# 漫文,評(píng)論留言中有很多朋友多次提到 Span,周末抽空看了下,確實(shí)是一個(gè)非常的新結(jié)構(gòu),讓我想到了當(dāng)年的WCF,它統(tǒng)一了.NET下各種零散的分布式技術(shù),包括:.NET Remoteing,WebService,NamedPipe,MSMQ,而這里的 Span 統(tǒng)一了 C# 進(jìn)程中的三大塊內(nèi)存訪問,包括:棧內(nèi)存, 托管堆內(nèi)存, 非托管堆內(nèi)存,畫個(gè)圖如下:

 

接下來就和大家具體聊聊這三大塊的內(nèi)存統(tǒng)一訪問。

二:進(jìn)程中的三大塊內(nèi)存解析

1. 棧內(nèi)存

大家應(yīng)該知道方法內(nèi)的局部變量是存放在棧上的,而且每一個(gè)線程默認(rèn)會(huì)被分配 1M 的內(nèi)存空間,我舉個(gè)例子:

  1. static void Main(string[] args) 
  2.         { 
  3.             int i = 10; 
  4.             long j = 20; 
  5.             List<string> list = new List<string>(); 
  6.         } 

上面 i,j 的值都是存于棧上,list的堆上內(nèi)存地址也是存于棧上,為了看個(gè)究竟,可以用 windbg 驗(yàn)證一下:

  1. 0:000> !clrstack -l 
  2. OS Thread Id: 0x2708 (0) 
  3.         Child SP               IP Call Site 
  4. 00000072E47CE558 00007ff89cf7c184 [InlinedCallFrame: 00000072e47ce558] Interop+Kernel32.ReadFile(IntPtr, Byte*, Int32, Int32 ByRef, IntPtr) 
  5. 00000072E47CE558 00007ff7c7c03fd8 [InlinedCallFrame: 00000072e47ce558] Interop+Kernel32.ReadFile(IntPtr, Byte*, Int32, Int32 ByRef, IntPtr) 
  6. 00000072E47CE520 00007FF7C7C03FD8 ILStubClass.IL_STUB_PInvoke(IntPtr, Byte*, Int32, Int32 ByRef, IntPtr) 
  7. 00000072E47CE7B0 00007FF8541E530D System.Console.ReadLine() 
  8. 00000072E47CE7E0 00007FF7C7C0101E DataStruct.Program.Main(System.String[]) [E:\net5\ConsoleApp2\ConsoleApp1\Program.cs @ 22] 
  9.     LOCALS: 
  10.         0x00000072E47CE82C = 0x000000000000000a 
  11.         0x00000072E47CE820 = 0x0000000000000014 
  12.         0x00000072E47CE818 = 0x0000018015aeab10 

通過 clrstack -l 查看線程棧,最后三行可以明顯的看到 0a -> 10, 14 -> 20 , 0xxxxxxb10 => list堆地址,除了這些簡單類型,還可以在棧上分配復(fù)雜類型,這里就要用到 stackalloc 關(guān)鍵詞, 如下代碼:

  1. int* ptr = stackalloc int[3] { 10, 11, 12 }; 

問題就在這里,指針類型雖然靈活,但是做任何事情都比較繁瑣,比如說:

  • 查找某一個(gè)數(shù)是否在 int[] 中
  • 反轉(zhuǎn) int[]
  • 剔除尾部的某一個(gè)數(shù)字(比如 12)

就拿第一個(gè)問題來說,操作指針的代碼如下:

  1. //指針接收 
  2.             int* ptr = stackalloc int[3] { 10, 11, 12 }; 
  3.  
  4.             //包含判斷 
  5.             for (int i = 0; i < 3; i++) 
  6.             { 
  7.                 if (*ptr++ == 11) 
  8.                 { 
  9.                     Console.WriteLine(" 11 存在 數(shù)組中"); 
  10.                 } 
  11.             } 

 

后面的兩個(gè)問題就更加復(fù)雜了,既然 Span 是統(tǒng)一訪問,就應(yīng)該用 Span 來接 stackalloc,代碼如下:

  1. Span<int> span = stackalloc int[3] { 10, 11, 12 }; 
  2.  
  3.             //1. 是否包含 
  4.             var hasNum = span.Contains(11); 
  5.  
  6.             //2. 反轉(zhuǎn) 
  7.             span.Reverse(); 
  8.  
  9.             //3. 剔除尾部 
  10.             span.Trim(12); 

這就很了,你既不需要接觸指針,又能完成指針的大部分操作,而且還特別便捷,佩服,最后來驗(yàn)證一下 int[] 是否真的在 線程棧 上。

  1. 0:000> !clrstack -l 
  2. 000000ED7737E4B0 00007FF7C4EA16AD DataStruct.Program.Main(System.String[]) [E:\net5\ConsoleApp2\ConsoleApp1\Program.cs @ 28] 
  3.     LOCALS: 
  4.         0x000000ED7737E570 = 0x000000ed7737e4d0 
  5.         0x000000ED7737E56C = 0x0000000000000001 
  6.         0x000000ED7737E558 = 0x000000ed7737e4d0 
  7.  
  8. 0:000> dp 0x000000ed7737e4d0 
  9. 000000ed`7737e4d0  0000000b`0000000c 00000000`0000000a 

從 Locals 處的 0x000000ED7737E570 = 0x000000ed7737e4d0 可以看到 key / value 是非常相近的,說明在棧上無疑。

從最后一行 a,b,c 可看出對(duì)應(yīng)的就是數(shù)組中的 10,11,12。

2. 非托管堆內(nèi)存

說到非托管內(nèi)存,讓我想起了當(dāng)年 C# 調(diào)用 C++ 的場景,代碼到處充斥著類似下面的語句:

  1. private bool SendMessage(int messageType, string ip, string port, int length, byte[] messageBytes) 
  2.         { 
  3.             bool result = false
  4.             if (windowHandle != 0) 
  5.             { 
  6.                 var bytes = new byte[Const.MaxLengthOfBuffer]; 
  7.                 Array.Copy(messageBytes, bytes, messageBytes.Length); 
  8.  
  9.                 int sizeOfType = Marshal.SizeOf(typeof(StClientData)); 
  10.  
  11.                 StClientData stData = new StClientData 
  12.                 { 
  13.                     Ip = GlobalConvert.IpAddressToUInt32(IPAddress.Parse(ip)), 
  14.                     Port = Convert.ToInt16(port), 
  15.                     Length = Convert.ToUInt32(length), 
  16.                     Buffer = bytes 
  17.                 }; 
  18.  
  19.  
  20.                 int sizeOfStData = Marshal.SizeOf(stData); 
  21.  
  22.                 IntPtr pointer = Marshal.AllocHGlobal(sizeOfStData); 
  23.  
  24.                 Marshal.StructureToPtr(stData, pointer, true); 
  25.  
  26.                 CopyData copyData = new CopyData 
  27.                 { 
  28.                     DwData = (IntPtr)messageType, 
  29.                     CbData = Marshal.SizeOf(sizeOfType), 
  30.                     LpData = pointer 
  31.                 }; 
  32.  
  33.                 SendMessage(windowHandle, WmCopydata, 0, ref copyData); 
  34.  
  35.                 Marshal.FreeHGlobal(pointer); 
  36.  
  37.                 string data = GlobalConvert.ByteArrayToHexString(messageBytes); 
  38.                 CommunicationManager.Instance.SendDebugInfo(new DataSendEventArgs() { Data = data }); 
  39.  
  40.                 result = true
  41.             } 
  42.             return result; 
  43.         } 

上面代碼中的: IntPtr pointer = Marshal.AllocHGlobal(sizeOfStData); 和 Marshal.FreeHGlobal(pointer) 就用到了非托管內(nèi)存,從現(xiàn)在開始你就可以用 Span 來接 Marshal.AllocHGlobal 分配的非托管內(nèi)存啦!,如下代碼所示:

  1. class Program 
  2.     { 
  3.         static unsafe void Main(string[] args) 
  4.         { 
  5.             var ptr = Marshal.AllocHGlobal(3); 
  6.  
  7.             //將 ptr 轉(zhuǎn)換為 span 
  8.             var span = new Span<byte>((byte*)ptr, 3) { [0] = 10, [1] = 11, [2] = 12 }; 
  9.  
  10.             //然后在  span 中可以進(jìn)行各種操作了。。。 
  11.  
  12.             Marshal.FreeHGlobal(ptr); 
  13.         } 
  14.     } 

這里我也用 windbg 給大家看一下 未托管內(nèi)存 在內(nèi)存中是個(gè)什么樣子。

  1. 0:000> !clrstack -l 
  2. OS Thread Id: 0x3b10 (0) 
  3.         Child SP               IP Call Site 
  4. 000000A51777E758 00007ff89cf7c184 [InlinedCallFrame: 000000a51777e758] Interop+Kernel32.ReadFile(IntPtr, Byte*, Int32, Int32 ByRef, IntPtr) 
  5. 000000A51777E758 00007ff7c4654dd8 [InlinedCallFrame: 000000a51777e758] Interop+Kernel32.ReadFile(IntPtr, Byte*, Int32, Int32 ByRef, IntPtr) 
  6. 000000A51777E720 00007FF7C4654DD8 ILStubClass.IL_STUB_PInvoke(IntPtr, Byte*, Int32, Int32 ByRef, IntPtr) 
  7. 000000A51777E9E0 00007FF7C46511D0 DataStruct.Program.Main(System.String[]) [E:\net5\ConsoleApp2\ConsoleApp1\Program.cs @ 26] 
  8.     LOCALS: 
  9.         0x000000A51777EA58 = 0x0000027490144760 
  10.         0x000000A51777EA48 = 0x0000027490144760 
  11.         0x000000A51777EA38 = 0x0000027490144760 
  12.  
  13. 0:000> dp 0x0000027490144760 
  14. 00000274`90144760  abababab`ab0c0b0a abababab`abababab         

最后一行的 0c0b0a 這就是低位到高位的 10,11,12 三個(gè)數(shù),接下來從 Locals 處 0x000000A51777EA58 = 0x0000027490144760 可以看出,這個(gè)key,value 相隔十萬八千里,說明肯定不在棧內(nèi)存中,繼續(xù)用 windbg 鑒別一下 0x0000027490144760 是否是托管堆上,可以用 !eeheap -gc 查看托管堆地址范圍,如下代碼:

  1. 0:000> !eeheap -gc 
  2. Number of GC Heaps: 1 
  3. generation 0 starts at 0x00000274901B1030 
  4. generation 1 starts at 0x00000274901B1018 
  5. generation 2 starts at 0x00000274901B1000 
  6. ephemeral segment allocation context: none 
  7.          segment             begin         allocated              size 
  8. 00000274901B0000  00000274901B1000  00000274901C5370  0x14370(82800) 
  9. Large object heap starts at 0x00000274A01B1000 
  10.          segment             begin         allocated              size 
  11. 00000274A01B0000  00000274A01B1000  00000274A01B5480  0x4480(17536) 
  12. Total Size:              Size: 0x187f0 (100336) bytes. 
  13. ------------------------------ 
  14. GC Heap Size:    Size: 0x187f0 (100336) bytes. 

從上面信息可以看到,0x0000027490144760 明顯不在:3代堆:00000274901B1000 ~ 00000274901C5370 和 大對(duì)象堆:00000274A01B1000 ~ 00000274A01B5480 區(qū)間范圍內(nèi)。

3. 托管堆內(nèi)存

用 Span 統(tǒng)一托管內(nèi)存訪問那是相當(dāng)簡單了,如下代碼所示:

Span span = new byte[3] { 10, 11, 12 };

同樣,你有了Span,你就可以使用 Span 自帶的各種方法,這里就不多介紹了,大家有興趣可以實(shí)操一下。

三:總結(jié)

總的來說,這一篇主要是從思想上帶大家一起認(rèn)識(shí) Span,以及如何用 Span 對(duì)接 三大區(qū)域內(nèi)存,關(guān)于 Span 的好處以及源碼解析,后面上專門的文章吧!

本文轉(zhuǎn)載自微信公眾號(hào)「 一線碼農(nóng)聊技術(shù)」,可以通過以下二維碼關(guān)注。轉(zhuǎn)載本文請(qǐng)聯(lián)系 一線碼農(nóng)聊技術(shù)公眾號(hào)。

 

 

責(zé)任編輯:武曉燕 來源: 一線碼農(nóng)聊技術(shù)
相關(guān)推薦

2021-11-01 07:50:44

TomcatWeb應(yīng)用

2022-06-06 07:52:00

Python大風(fēng)車

2021-10-08 13:38:23

手機(jī)系統(tǒng)鴻蒙

2023-03-03 09:11:55

軟件開發(fā)NASA

2022-04-08 08:11:28

Python代碼

2018-05-14 22:58:14

戴爾

2021-03-01 12:06:12

Nginx命令Linux

2018-04-11 14:30:33

2017-02-23 08:00:04

智能語音Click

2025-01-09 11:10:15

2011-04-13 16:50:54

CC++內(nèi)存

2011-06-21 11:16:24

cc++

2023-05-06 06:47:46

Bing聊天機(jī)器人

2023-09-08 09:12:57

內(nèi)存緩存圖像

2024-02-26 12:42:40

2019-02-12 11:07:49

2023-11-01 08:07:42

.NETC#

2021-12-27 07:59:50

ECMAScript JSON模塊Node.js
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)

主站蜘蛛池模板: 国产精品1区2区 | 天天操天天干天天透 | 欧美色综合 | 特黄色毛片 | 亚洲视频中文字幕 | 9久9久9久女女女九九九一九 | 国产免费播放视频 | 美女在线视频一区二区三区 | 精品久久久久久久久久久 | 中文字幕一区二区三区四区 | 国产精品性做久久久久久 | 成人免费共享视频 | 欧美精品一区二区三区一线天视频 | 视频一区二区三区中文字幕 | 久草免费在线视频 | 四虎成人精品永久免费av九九 | 国产成人在线一区二区 | 日韩成人免费中文字幕 | 亚洲精品久久久久久国产精华液 | 九九综合九九 | 久久精品国产一区 | 久国产精品 | 一区二区三区免费 | 秋霞国产 | 在线观看视频你懂得 | 九九久久久 | 91国自视频 | 一级黄色在线 | 中文字幕一区二区三区日韩精品 | 黄色网址在线播放 | 久久久蜜臀国产一区二区 | 天天天插| 永久免费在线观看 | 日本a视频| 91精品在线播放 | 精品国产欧美 | 日韩色图在线观看 | 999精彩视频 | 毛片视频免费 | 中文字幕福利视频 | 久久免费国产视频 |