.NET中堆棧和堆的特點(diǎn)與差異介紹
一、前言
.NET提供了垃圾回收機(jī)制,使程序員從內(nèi)存管理中被解放出來。但這并不代表程序員就無須了解分配的對象是如何被回收的。更重要的是,一些非托管的資源仍然需要程序員小心地分配與回收。
理解堆和堆棧是理解內(nèi)存管理的基礎(chǔ)。每一個(gè).NET程序都最終會(huì)運(yùn)行在一個(gè)操作系統(tǒng)進(jìn)程中,假設(shè)這個(gè)操作系統(tǒng)是傳統(tǒng)的32位的,那每個(gè).NET程序都可以擁有一個(gè)4GB的虛擬內(nèi)存。.NET會(huì)在這個(gè)4GB的內(nèi)存塊中開辟出三塊內(nèi)存分別作為堆棧、受托管的堆和非托管的堆。
二、.NET中的堆棧
.NET中的堆棧用來存儲(chǔ)值類型的對象和引用類型對象的引用,堆棧的分配是連續(xù)的,在.NET程序中,始終存儲(chǔ)了一個(gè)特殊的指針指向堆棧的尾部,這樣一個(gè)堆棧內(nèi)存的分配就直接從這個(gè)指針指向的內(nèi)存位置開始向下分配。下圖展示了.NET的堆棧分配方式。
如上圖所示,堆棧上的地址從高位開始往低位分配內(nèi)存,.NET只需要保存一個(gè)堆棧指針指向下一個(gè)未分配內(nèi)存的內(nèi)存地址。對于所有需要分配的對象,依次分配到堆棧中,其釋放也嚴(yán)格按照棧的邏輯,依次進(jìn)行退棧。這里提到的“依次”,是指按照變量的作用域進(jìn)行的??紤]下面的代碼:
ClassA a = new ClassA(); a.intA = 1; a.intB = 2;
這里假設(shè)ClassA是一個(gè)引用類型,則堆棧中依次需要分配的是a的引用、a.intA和a.intB。當(dāng)a的作用域結(jié)束后,這三個(gè)變量則從堆棧中依次退出:a.intB、a.intA,然后才是a。
三、.NET中的托管堆
接下來我們看一下托管的堆。.NET中的引用類型對象是分配在托管堆上的。通常我們稱.NET中的堆,指的就是托管的堆。和堆棧一樣,托管的堆也是進(jìn)程內(nèi)存空間中的一塊區(qū)域。但托管堆中的內(nèi)存的分配卻和堆棧有很大的區(qū)別。受益于.NET的內(nèi)存管理機(jī)制,托管堆的分配也是連續(xù)的,但是堆中存在著暫時(shí)不能被分配卻已經(jīng)無用的對象內(nèi)存塊。當(dāng)一個(gè)引用類型對象被初始化時(shí),就會(huì)通過指向堆上可用空間的指針分配一塊連續(xù)的內(nèi)存,然后使堆棧上的引用指向堆上的這塊內(nèi)存塊。下圖展示了堆的分配方式。
如上圖所示,程序通過分配在堆棧中的引用來找到分配在托管堆的對象實(shí)例。當(dāng)堆棧區(qū)域中的引用退出作用域時(shí),就僅僅斷開引用和實(shí)際對象的聯(lián)系。而當(dāng)托管堆中的內(nèi)存不夠時(shí),.NET開始執(zhí)行垃圾回收。垃圾回收是一個(gè)非常復(fù)雜的過程,它不僅涉及托管堆中對象的釋放,而且需要移動(dòng)合并托管堆中的內(nèi)存塊。當(dāng)垃圾回收后,堆內(nèi)不被使用的對象才會(huì)被部分釋放,而在這之前,它們在堆內(nèi)是暫時(shí)不可用的。
四、.NET中的非托管堆
.NET的程序還包含了非托管的堆,所有需要分配堆內(nèi)存的非托管資源將會(huì)被分配到非托管堆上。非托管的堆需要程序員用指針手動(dòng)地分配并且手動(dòng)地釋放,.NET的垃圾回收和內(nèi)存管理制度不適用于非托管的堆。
五、堆棧、托管堆和非托管堆的比較
堆棧、托管堆和非托管堆的分配各有特點(diǎn)。堆棧的內(nèi)存是連續(xù)分配的,按照作用域依次分配和釋放。堆棧的機(jī)制非常簡單,.NET依靠一個(gè)堆棧指針就可以進(jìn)行內(nèi)存操作,分配一個(gè)對象和釋放一個(gè)對象的大部分操作就是自增或者自減堆棧指針。.NET中的值類型對象和引用類型對象的引用是分配在堆棧內(nèi)的。
托管堆的內(nèi)存分配雖然也是連續(xù)的,但它卻比堆棧復(fù)雜得多。一塊堆內(nèi)存的分配需要涉及很多.NET內(nèi)存管理機(jī)制的內(nèi)部操作,另外當(dāng)堆內(nèi)存不夠時(shí),垃圾回收的執(zhí)行代價(jià)也是非常大的。相對于堆棧來說,堆的分配效率低得多。.NET中的引用類型對象是分配在托管堆上的,這些對象通過分配在堆棧上的引用來進(jìn)行訪問。
非托管堆和托管堆的區(qū)別在于非托管堆不受.NET的管理。非托管堆的內(nèi)存是由程序員手動(dòng)分配和釋放的,垃圾回收機(jī)制不適用于非托管堆,內(nèi)存塊也不會(huì)被合并移動(dòng),所以非托管堆的內(nèi)存分配按塊的,不連續(xù)的。
六、總結(jié)
.NET程序在進(jìn)程內(nèi)存中分配出堆棧、托管堆和非托管堆。所有的值類型對象和引用類型對象的引用都分配在堆棧上,堆棧根據(jù)對象的生存周期來依次分配和釋放,堆棧根據(jù)一個(gè)指向棧尾的指針來分配內(nèi)存,效率很高。
.NET所有的引用類型對象分配在托管堆上,托管堆連續(xù)分配內(nèi)存,并且受.NET的垃圾收集機(jī)制管理,受托管堆的內(nèi)存分配和釋放涉及復(fù)雜的內(nèi)存管理,效率相對于堆棧來說低得多。
需要分配堆內(nèi)存的非托管類型將被分配在非托管堆上,非托管堆不受.NET垃圾收集機(jī)制管理,內(nèi)存塊完全由程序員手動(dòng)申請和釋放。
以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
asp.net下實(shí)現(xiàn)支持文件分塊多點(diǎn)異步上傳的 Web Services
asp.net下實(shí)現(xiàn)支持文件分塊多點(diǎn)異步上傳的 Web Services...2007-04-04asp.net core webapi文件上傳功能的實(shí)現(xiàn)
這篇文章主要介紹了asp.net core webapi文件上傳功能的實(shí)現(xiàn),本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-12-12ASP.NET Core快速入門之實(shí)戰(zhàn)篇
這篇文章主要介紹了ASP.NET Core快速入門之實(shí)戰(zhàn)篇,對跨平臺(tái)框架感興趣的同學(xué),可以參考下2021-04-04ASP.NET中RadioButtonList綁定后臺(tái)數(shù)據(jù)后觸發(fā)點(diǎn)擊事件
這篇文章主要介紹了ASP.NET中RadioButtonList綁定后臺(tái)數(shù)據(jù)后觸發(fā)點(diǎn)擊事件的相關(guān)資料,感興趣的小伙伴們可以參考一下2016-05-05