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