.NET?Core內(nèi)存結(jié)構(gòu)體系(Windows環(huán)境)底層原理解析
物理內(nèi)存與虛擬內(nèi)存物理內(nèi)存
- 物理內(nèi)存(Physical Memory)
定義:物理內(nèi)存是計(jì)算機(jī)硬件中的實(shí)際RAM(如DDR5內(nèi)存條),直接通過(guò)總線與CPU連接,用于臨時(shí)存儲(chǔ)運(yùn)行中的程序和數(shù)據(jù)。 - 虛擬內(nèi)存(Virtual Memory)
定義:由操作系統(tǒng)管理的抽象內(nèi)存層,通過(guò)結(jié)合物理內(nèi)存和磁盤(pán)空間(如頁(yè)面文件或交換分區(qū)),為程序提供連續(xù)且獨(dú)立的內(nèi)存空間。
用戶只需要與虛擬內(nèi)存地址打交道,而無(wú)需關(guān)心數(shù)據(jù)到底分配在哪里
眼見(jiàn)為實(shí)
物理頁(yè)4K對(duì)齊
在Windows系統(tǒng)下,以4K為最小粒度,這個(gè)單位叫做物理頁(yè)
,并以4K的整數(shù)倍分配內(nèi)存。比如申請(qǐng)1k分配4k,申請(qǐng)5k分配8k
眼見(jiàn)為實(shí)
void page4k() { for (int i = 0; i < 200; i++) { //1k 的占用 LPVOID ptr = VirtualAlloc(NULL, 1024 * 1, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE); printf("i=%d, 1k, address:%#0.8x \n", i + 1, ptr); } for (int i = 200; i < 400; i++) { //5k 的占用 LPVOID ptr = VirtualAlloc(NULL, 1024 * 5, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE); printf("i=%d, 5k, address:%#0.8x \n", i + 1, ptr); } getchar(); }
申請(qǐng)1k分配4k
申請(qǐng)5k分配8k
物理內(nèi)存與虛擬內(nèi)存如何映射?
Windows系統(tǒng)采用二叉樹(shù)結(jié)構(gòu)
(5層)來(lái)實(shí)現(xiàn)高效映射。
舉個(gè)例子,某個(gè)32bit的內(nèi)存地址為:0x77b01a42,其二進(jìn)制為:01110,11110,11000,00001,101001000010
前20位
用來(lái)構(gòu)建頁(yè)表樹(shù)
,實(shí)現(xiàn)物理頁(yè)的的高效映射后12位
映射物理頁(yè)的偏移量
操作系統(tǒng)以4K為一個(gè)單位對(duì)內(nèi)存進(jìn)行分組,4G內(nèi)存=102410241024*4/(4/1024)=1048576物理頁(yè),如此龐大的物理頁(yè),,采用5層二叉樹(shù)來(lái)提高索引效率
眼見(jiàn)為實(shí):以notepad為例
任務(wù)管理:
Windbg:
可以看到非常明顯的不同,任務(wù)管理器顯示占用44.6mb內(nèi)存,而windbg顯示占用489.531mb內(nèi)存,這是為什么呢?答:顯示邏輯不同,任務(wù)管理器顯示的是Private WorkingSet,指的是物理內(nèi)存的地址,即內(nèi)存條上的內(nèi)存
,而Windbg是顯示映射到的物理頁(yè),Commit指的是虛擬內(nèi)存地址,這包括內(nèi)存條上的內(nèi)存,pagefile,image
三種
眼見(jiàn)為實(shí):可視化觀察 虛擬地址=>物理地址
使用windbg進(jìn)入內(nèi)核態(tài),這很重要,大家可以猜猜原因。
隨便找一個(gè)字符串的內(nèi)存地址
- 使用dp觀察虛擬地址
- 使用!vtop 觀察映射信息
- 使用!db觀察物理地址
眼見(jiàn)為實(shí):空指針區(qū)與用戶態(tài)區(qū)
windows/linux在默認(rèn)情況下,會(huì)開(kāi)啟ASLR,需要關(guān)閉此技術(shù)才能復(fù)現(xiàn)。ASLR 是一種針對(duì)緩沖區(qū)溢出攻擊等內(nèi)存攻擊技術(shù)而設(shè)計(jì)的安全特性。在沒(méi)有 ASLR 的情況下,程序加載到內(nèi)存中的位置通常是固定的,攻擊者可以預(yù)測(cè)程序中各種模塊(如可執(zhí)行文件、動(dòng)態(tài)鏈接庫(kù)等)的加載地址,進(jìn)而利用這些固定地址來(lái)構(gòu)造惡意代碼進(jìn)行攻擊,比如在緩沖區(qū)溢出攻擊中精準(zhǔn)定位跳轉(zhuǎn)地址來(lái)執(zhí)行惡意指令。而啟用 ASLR 后,操作系統(tǒng)在每次啟動(dòng)程序時(shí)會(huì)隨機(jī)化程序的內(nèi)存布局,包括可執(zhí)行文件、動(dòng)態(tài)鏈接庫(kù)、堆、棧等的加載地址,使得攻擊者難以準(zhǔn)確預(yù)測(cè)內(nèi)存地址,大大增加了攻擊的難度。
Reserved與Commit
- Reserved
在虛擬地址上申請(qǐng)一段內(nèi)存空間,此時(shí)操作系統(tǒng)也會(huì)同步創(chuàng)建頁(yè)表樹(shù)
,但此時(shí)并未映射到物理內(nèi)存
,此時(shí)對(duì)該虛擬內(nèi)存的讀寫(xiě)會(huì)拋異常 - Commit
給頁(yè)表樹(shù)
調(diào)配真實(shí)的物理內(nèi)存
,此時(shí)才能正常寫(xiě)入
眼見(jiàn)為實(shí):Reserved
void mem_reserved() { LPVOID ptr = VirtualAlloc(NULL, 4 * 1024, MEM_RESERVE, PAGE_READWRITE); *(int*)(ptr) = 10; //在首地址上寫(xiě)入內(nèi)容。 printf("num=%d", *(int*)ptr); }
眼見(jiàn)為實(shí):Commit
void mem_commit() { LPVOID ptr = VirtualAlloc(NULL, 4 * 1024, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE); *(int*)(ptr) = 10; //在首地址上寫(xiě)入內(nèi)容。 printf("num=%d", *(int*)ptr); }
NT堆
NT堆是 Windows NT 內(nèi)核引入的內(nèi)存管理組件,主要負(fù)責(zé)進(jìn)程內(nèi)的堆內(nèi)存分配與釋放。在 Windows 系統(tǒng)里,進(jìn)程可以使用 NT 堆來(lái)動(dòng)態(tài)分配和管理內(nèi)存,比如程序中使用 malloc()(C 語(yǔ)言)、new(C++) 等函數(shù)進(jìn)行內(nèi)存分配時(shí),底層通常就依賴 NT 堆機(jī)制。
上面說(shuō)到,VirtualAlloc方法它會(huì)一次性分配 64k 整數(shù)倍的內(nèi)存段,內(nèi)部對(duì)象按4k的內(nèi)存頁(yè)對(duì)齊.如果讓application直接操作VirtualAlloc,難免會(huì)造成大量的內(nèi)存浪費(fèi)。為了提高內(nèi)存性能與使用效率,Windows又提供了一層抽象
,以提供更細(xì)顆粒度的內(nèi)存管理。它的名字叫做NT堆
- 在32bit平臺(tái)上:8byte為一個(gè)分配粒度
- 在64bit平臺(tái)上:16btye為一個(gè)分配粒度
- CRT堆:C運(yùn)行時(shí)使用的堆,默認(rèn)是對(duì)NT堆的簡(jiǎn)單封裝
- 托管堆:用作特殊用途的,自行實(shí)現(xiàn)的一套內(nèi)存池管理機(jī)制。比如GC堆
從圖中可以看出,使用NT與否取決于程序員本身。完全可以繞過(guò)NT堆,直接使用VirtualAlloc來(lái)分配內(nèi)存,只要你接收內(nèi)存浪費(fèi)。
眼見(jiàn)為實(shí):GC堆,底層使用VirtualAlloc分配內(nèi)存
static void Main(string[] args) { var rand = new Random(); List<string> list = new List<string>(); for (int i = 0; i < 100000; i++) { var str = string.Join(",", Enumerable.Range(0, rand.Next(1, 1000))); list.Add(str); Console.WriteLine($"i={i},length={str.Length}"); } Console.ReadLine(); }
在bp KERNELBASE!VirtualAlloc 下斷點(diǎn)
眼見(jiàn)為實(shí):CRT堆/NT堆,底層使用VirtualAlloc分配內(nèi)存
? #include <iostream> #include <Windows.h> void crt_c() { for (int i = 0; i < 10000000; i++) { int* ptr = (int*)malloc(sizeof(int) * 1000); *(ptr) = 10; printf("第 %d 次分配 \n", i); } } ?
在 bp ntdll!NtAllocateVirtualMemory 下斷點(diǎn)
到此這篇關(guān)于.NET Core內(nèi)存結(jié)構(gòu)體系(Windows環(huán)境)底層原理淺談的文章就介紹到這了,更多相關(guān).NET Core內(nèi)存結(jié)構(gòu)體系內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
.NET一行代碼實(shí)現(xiàn)GC調(diào)優(yōu),讓程序不再占用內(nèi)存
這篇文章主要介紹了NET一行代碼實(shí)現(xiàn)GC調(diào)優(yōu),讓程序不再占用內(nèi)存的相關(guān)資料,需要的朋友可以參考下2022-11-11ASP.NET.4.5.1+MVC5.0設(shè)置系統(tǒng)角色與權(quán)限(二)
這篇文章主要介紹了使用ASP.NET.4.5.1+MVC5.0構(gòu)建項(xiàng)目中設(shè)置系統(tǒng)角色的全部過(guò)程,十分的詳細(xì),附上全部源碼,推薦給想學(xué)習(xí).net+mvc的小伙伴們2015-01-01asp.net 驗(yàn)證碼的簡(jiǎn)單制作(vb.net+C#)
asp.net中實(shí)現(xiàn)簡(jiǎn)單驗(yàn)證碼的方法,需要的朋友可以參考下2012-05-05ASP.NET?MVC實(shí)現(xiàn)本地化和全球化
這篇文章介紹了ASP.NET?MVC實(shí)現(xiàn)本地化和全球化的方法,文中通過(guò)示例代碼介紹的非常詳細(xì)。對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2022-10-10.NET?Core實(shí)現(xiàn)企業(yè)微信消息推送
這篇文章介紹了.NET?Core實(shí)現(xiàn)企業(yè)微信消息推送的方法,文中通過(guò)示例代碼介紹的非常詳細(xì)。對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2022-06-06ASP.NET通過(guò)byte正確安全的判斷上傳文件格式
本文介紹一種更安全的方式上傳圖片,他能有效的防止一些通過(guò)修改文件后綴或MIME來(lái)偽造的圖片的上傳,從而保證服務(wù)器的安全,希望對(duì)大家有所幫助。2016-03-03