ASP.Net?Core中的內(nèi)存和GC機(jī)制
托管代碼
在 .NET 中, CLR(Common Language Runtime) 負(fù)責(zé)提取托管代碼并編譯成機(jī)器語言,然后執(zhí)行它。在此過程中,CLR 提供自動內(nèi)存管理、安全邊界、類型安全等服務(wù),保證了代碼安全。
托管代碼指在其執(zhí)行過程中由 CLR(Common Language Runtime) 管理的代碼,托管代碼是可在 .NET 上運(yùn)行得一種高級語言(C#、F#等),編寫的托管代碼被編譯后會被生成 中間語言(IL)。
CLR 有 .NET Core/.NET5+、Mono、.NET Framework 等實現(xiàn),托管代碼生成的文件(IL代碼)不能被操作系統(tǒng)直接運(yùn)行,需要 CLR 的實現(xiàn)(如 .NET5) 托管運(yùn)行,托管過程中對其再次編譯生成二進(jìn)制代碼(JIT編譯)。
中間語言(IL)有時也稱為公共中間語言 (CIL) 或 Microsoft 中間語言 (MSIL)。
自動內(nèi)存管理
自動內(nèi)存管理是 CLR 的功能之一,它可以為應(yīng)用程序管理內(nèi)存的分配和釋放,托管代碼被執(zhí)行時,由 CLR 進(jìn)行內(nèi)存管理,保證了內(nèi)存安全。
垃圾回收
GC
GC(garbage collector)中文譯為垃圾回收器,.NET 中的 GC 指的是 CLR 中的自動內(nèi)存管理器,GC 負(fù)責(zé)管理 .NET 程序的內(nèi)存分配和釋放。
GC 的優(yōu)點如下:
自動管理內(nèi)存,不必手動分配和釋放;
高效管理托管堆上的對象;
智能回收對象,清除內(nèi)存;
內(nèi)存安全:避免野指針、懸空指針等情況造成嚴(yán)重錯誤;
內(nèi)存
物理內(nèi)存
物理內(nèi)存是物理內(nèi)存條上的內(nèi)存空間,是物理機(jī)器真實的容量大小。
虛擬內(nèi)存
虛擬內(nèi)存(Virtual Memory)是計算機(jī)操作系統(tǒng)進(jìn)行內(nèi)存管理的一種技術(shù),它可以將多個硬件、非連續(xù)地址的碎片空間組合起來,形成進(jìn)程上可識別的連續(xù)內(nèi)存空間。
虛擬內(nèi)存由操作系統(tǒng)進(jìn)行支持,如 Windows 上的虛擬內(nèi)存,Linux 上的交互空間,虛擬內(nèi)存需要操作系統(tǒng)映射到真實的內(nèi)存地址空間才能使用。虛擬內(nèi)存調(diào)度方式有分頁式、段式、段頁式3種,讀者感興趣可自行查閱資料。
現(xiàn)代操作系統(tǒng)都采用了虛擬內(nèi)存管理技術(shù),通過對物理存儲設(shè)備的抽象,操作系統(tǒng)調(diào)度外存當(dāng)作內(nèi)存使用,提供了比物理內(nèi)存更大的內(nèi)存范圍。
這些存儲設(shè)備組成的內(nèi)存稱為虛擬地址空間,而用戶(開發(fā)者)接觸到的地址是虛地址,并不是真實的物理地址。虛擬空間大大拓展了內(nèi)存,使得系統(tǒng)可以同時運(yùn)行多道程序而不“吃力”。
虛擬地址空間分為兩部分:用戶空間、內(nèi)核空間,每個程序運(yùn)行時的會消耗兩種空間。在 Linux 中比例是 3:1,在 Windows 中是 2:2。
.NET 內(nèi)存組成
.NET 中,內(nèi)存分為非托管內(nèi)存、托管內(nèi)存。
.NET Core/.NET5+ 有一個稱為 dotnet 的驅(qū)動程序,此驅(qū)動程序用于執(zhí)行命令或運(yùn)行 .NET 程序。當(dāng)我們使用 dotnet 命令運(yùn)行一個 .dll 文件時,操作系統(tǒng)會啟動 dotnet 驅(qū)動程序,此時會分配操作系統(tǒng)內(nèi)存資源、dotnet 驅(qū)動程序內(nèi)存資源,這一部分即非托管資源,其中 dotnet 部分的內(nèi)存包含了 CLR 等部件的內(nèi)存。即使你并沒有使用到 C/C++ 等非托管代碼或者使用非托管資源,也會使用到非托管內(nèi)存。
接下來 CLR 將初始化新進(jìn)程,CLR 將為其分配托管內(nèi)存(托管堆),這段托管內(nèi)存是一個連續(xù)的地址空間區(qū)域。.NET 安全代碼只能使用托管內(nèi)存,不能直接使用物理內(nèi)存,垃圾收集器會為安全代碼在托管堆上分配和釋放虛擬內(nèi)存。
顯然, dotnet 的工作原理十分復(fù)雜,筆者沒有能力講清楚,感興趣的讀者可以自行查閱資料。
CLR 中的內(nèi)存
微軟 .NET CLR 文檔中寫道:By default, on 32-bit computers, each process has a 2-GB user-mode virtual address space.
即在 32 位系統(tǒng)中,.NET 進(jìn)程會使用 2GB 的用戶模式虛擬內(nèi)存,其虛擬地址空間的表示范圍是 0x00000000 到 0x7fff;而 64 位系統(tǒng)中,地址范圍是 0x000'00000000 到0x7FFF'FFFFFFFF,約等于 16TB。
從以上信息,我們知道 .NET 程序會消耗比較多的虛擬內(nèi)存,如果在 64 位操作系統(tǒng)上運(yùn)行 .NET 程序,其用戶模式虛擬地址空間可能遠(yuǎn)遠(yuǎn)大于 2GB。
編寫一個 "c1" 程序,其代碼如下:
static void Main(string[] args) { Console.WriteLine("Hello World!"); Console.Read(); }
在 Linux 中使用 dotnet xx.dll 命令運(yùn)行程序,然后查看其占用的資源:
VIRT RES SHR S %CPU %MEM TIME+ COMMAND 3.1g 0.0g 0.0g S 0.3 0.3 0:00.83 dotnet
使用 dotnet-counters 查看 dotnet 進(jìn)程:
GC Heap Size (MB) 0 Gen 0 GC Count (Count / 1 sec) 0 Gen 0 Size (B) 0 Gen 1 GC Count (Count / 1 sec) 0 Gen 1 Size (B) 0 Gen 2 GC Count (Count / 1 sec) 0 Gen 2 Size (B) 0 LOH Size (B) 0
注:使用 dotnet run 運(yùn)行 .NET 項目,會出現(xiàn) dotnet、c1 兩個進(jìn)程,可以看到會產(chǎn)生 dotnet 和 c1 兩個進(jìn)程,dotnet 是驅(qū)動程序,dotnet 啟動后,CLR 會將. dll 程序集編譯,并初始化啟動一個進(jìn)程。
CLR 中的虛擬地址空間需要位于一個地址塊中,因為在請求虛擬內(nèi)存分配時,虛擬內(nèi)存管理器必須找到滿足需求的單個可用塊,例如就算存在大于 2GB 的虛擬地址空間,但如果不是連續(xù)的,則會分配失敗。如果沒有足夠的可供保留的虛擬地址空間或可供提交的物理空間,則可能會用盡內(nèi)存。
CLR 虛擬內(nèi)存狀態(tài)
CLR 中的虛擬內(nèi)存可以有三種狀態(tài):
State | Description |
---|---|
Free 可用 | The block of memory has no references to it and is available for allocation. 內(nèi)存塊沒有對它的引用,可以進(jìn)行分配 |
Reserved保留 | The block of memory is available for your use and cannot be used for any other allocation request. 該內(nèi)存塊可供您使用,不能用于任何其他分配請求 However, you cannot store data to this memory block until it is committed. 但是,在提交數(shù)據(jù)之前,不能將數(shù)據(jù)存儲到此內(nèi)存塊中 |
Committed已提交 | The block of memory is assigned to physical storage. 內(nèi)存塊已指派給物理存儲 |
內(nèi)存分配
CLR 在初始化新進(jìn)程時,會為進(jìn)程保留一個連續(xù)的地址空間區(qū)域,這個地址空間被稱為托管堆。托管堆中維護(hù)著一個指針,最初此指針指向托管堆的基址,這個指針是向后移動的。當(dāng)需要分配內(nèi)存時,CLR 便會分配位于此指針后的內(nèi)存區(qū)域,同時指針指向此對象地址空間之后的位置。
由于 CLR 通過向指針添加值來為對象分配內(nèi)存,所以它的分配速度幾乎跟從堆棧中分配內(nèi)存速度一樣快;而且連續(xù)分配的新對象連續(xù)存儲在托管堆中,程序可以快速地訪問這些對象。
當(dāng) GC 回收內(nèi)存時,一些對象釋放后內(nèi)存會被回收,這樣托管堆地內(nèi)存處于碎片化,之后整個內(nèi)存段會被壓縮,重新組成連連續(xù)的內(nèi)存段,指針會被重置到對象的末尾。
當(dāng)然,大對象堆(LOH)回收并不會壓縮內(nèi)存段,這一點我們后面再討論。
內(nèi)存釋放
垃圾回收的條件
根據(jù)微軟官方文檔,整理的垃圾回收條件如下:
- 系統(tǒng)物理內(nèi)存不足;
- 托管堆分配的內(nèi)存已超出可接受閾值;(當(dāng)然,這個閾值會被動態(tài)調(diào)整)
- 手動調(diào)用 GC 類的 API(例如 GC.Collect);
托管堆
本機(jī)堆(Native Heap)
前面提到過,.NET 的內(nèi)存有非托管內(nèi)存和托管內(nèi)存。CLR 運(yùn)行的進(jìn)程,存在本機(jī)堆和托管堆兩種內(nèi)存堆,本機(jī)內(nèi)存堆通過 Windows API 的 VirtualAlloc 函數(shù)分配,提供給 操作系統(tǒng)和 CLR 使用,用于非托管代碼所需的內(nèi)存。
托管堆(Managed Heap)
關(guān)于托管堆,前面已經(jīng)寫了,這里不再贅述。
托管堆代數(shù)
托管堆中的內(nèi)存被分為三代,分別使用0、1、2 標(biāo)識,GC 分配的內(nèi)存首先在 0 代托管堆中,當(dāng)進(jìn)行垃圾回收時,如果對象沒有被釋放,則將其升級并存儲到 1 代托管堆中。1 代托管堆進(jìn)行內(nèi)存回收時,不被釋放的對象也會被升級到 2 代內(nèi)存中,然后 1 代內(nèi)存堆進(jìn)行空間壓縮。
托管堆的管理是 GC 負(fù)責(zé)的,而 GC 進(jìn)行內(nèi)存分配和釋放,使用了 GC 算法。
GC 算法基于以下理論:
- ① 壓縮托管堆的一部分內(nèi)存要比壓縮整個托管堆速度快;
- ② 較新的對象生命周期較短,較舊的對象生命周期較長;
- ③ 較新的對象趨向于相互關(guān)聯(lián),并且大約在同一時間被應(yīng)用程序訪問;
我們必須深刻理解這些理論,才能深入理解托管堆的設(shè)計。
關(guān)于 0 到 2 代堆,其基本說明如下:
- 0 代:0 代中的對象擁有短暫的生命周期,垃圾回收最常發(fā)生在此代中;
- 1 代:作為生命周期較短和生命周期較長對象的緩沖區(qū)。
- 2 代:存儲生命周期長的對象;0、1 代沒被回收而升級的對象會升級到 2 代中,靜態(tài)數(shù)據(jù)等則會一開始就分配到 2代。
在 .NET 5 之前,.NET 有 SOH(小對象堆)、LOH(大對象堆);在 .NET 5 中,出現(xiàn)了 POH ;
小對象堆的內(nèi)存段有 0、1、2 代堆;
以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
詳解.NET Core 使用HttpClient SSL請求出錯的解決辦法
這篇文章主要介紹了.NET Core 使用HttpClient SSL請求出錯的解決辦法,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2019-03-03ASP.NET下使用WScript.Shell執(zhí)行命令
ASP.NET下有自己的執(zhí)行CMD命令的方式,這里用WScript.Shell似有畫蛇添足之嫌,但是我們也不能排除真的有機(jī)器禁用了.NET的相關(guān)類,未雨綢繆嘛。當(dāng)然也不僅僅局限于WScript.Shell,只要是ASP中能用的組件,統(tǒng)統(tǒng)都可以用于ASP.NET中,而且還更方便!2008-05-05asp.net連接數(shù)據(jù)庫讀取數(shù)據(jù)示例分享
這篇文章主要介紹了asp.net連接數(shù)據(jù)庫讀取數(shù)據(jù)示例,大家參考使用吧2014-01-01asp.net中SqlCacheDependency緩存技術(shù)概述
這篇文章主要介紹了asp.net中SqlCacheDependency緩存技術(shù)概述,是大型web程序設(shè)計中常用的技術(shù),本文對此進(jìn)行了較為詳細(xì)的描述,需要的朋友可以參考下2014-08-08.NET?實現(xiàn)啟動時重定向程序運(yùn)行路徑及?Windows?服務(wù)運(yùn)行模式部署的方法
這篇文章主要介紹了.NET?實現(xiàn)啟動時重定向程序運(yùn)行路徑及?Windows?服務(wù)運(yùn)行模式部署,本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2022-09-09