目錄:算法
21.1 託管堆基礎數組
21.2 代:提高性能服務器
21.3 使用須要特殊清理的類型網絡
在面向對象的環境中,每一個類型都表明可供程序使用的一種資源。要使用這些資源,必須爲表明資源的類型分配內存,訪問一個資源所需步驟:數據結構
1.調用IL指令newobj,爲表明資源的類型分配內存(通常使用C#new操做符來完成)。併發
2.初始化內存,設置資源的初始狀態並使資源可用。類型的實例構造器負責設置初始狀態。性能
3.訪問類型的成員來使用資源(有必要能夠重複)。優化
4.推毀資源的狀態以進行清理。this
5.釋放內存。垃圾回收器獨自負責這一步。spa
1.1 從託管堆分配資源
CLR要求全部對象都從託管堆分配。進程初始化時,CLR劃出一個地址空間區域做爲託管堆。CLR還要維護一個指針(NextObjPtr),該指針指向下一個對象在堆中的分配位置。剛開始的時候,NextObjPtr設爲地址空間區域的基地址。
一個區域被非垃圾對象填滿後,CLR會分配更多的區域。這個過程一直重複,直至整個進程地址空間都被填滿。(因此應用程序的內存受進程的虛擬地址空間的限制)
C#的new操做符致使CLR執行如下步驟:
1.計算類型的字段(以及從基類型繼承的字段)所須要的字節數。
2.加上對象的開銷所須要的字節數。每一個對象都有兩個開銷字段:類型對象指針和同步塊索引。
3.CLR檢查區域中是否有分配對象所需的字節數。若是託管堆有足夠的可用空間,就在NextObjStr指針指向的地址處放入對象,爲對象分配的字節會被清零。接着調用類型構造器(爲this參數傳遞NextObjPtr),new操做符返回對象引用。就在返回這個引用以前,NextObjStr指針的值會加上對象佔用的字節數來獲得一個新值,即下個對象放入托管堆的地址。
對於託管堆,分配對象只需在指針上加一個值-速度至關快。
1.2 垃圾回收算法
沒有足夠地址空間來分配對象時,CLR就執行垃圾回收。
CLR使用引用跟蹤算法。引用跟蹤算法只關心引用類型的變量,由於只有這種變量才能引用堆上的對象;值類型變量直接包含值類型實例。引用類型變量可在許多場合使用,包括類的靜態和實例字段,或者方法的參數和局部變量。引用類型的變量稱爲-根。
CLR開始GC時,首先暫停進程中的全部線程。這樣能夠防止線程在CLR檢查期間訪問對象並更改其狀態。而後,CLR進入GC的標記階段。在這個階段,CLR遍歷堆中的全部對象,將同步塊索引字段中的一位設爲0。這代表全部對象都應刪除。而後,CLR檢查全部活動根,查看它們引用了哪些對象。若是一個根包含null,CLR忽略這個根並繼續檢查下個根。
任何根若是引用了堆上的對象,CLR都會標記那個對象,也就是將該對象的同步塊索引中的位設爲1。一個對象被標記後,CLR會檢查那個對象中的根,標記它們引用的對象。若是發現對象已經標記,就不從新檢查對象的字段。這就避免了因循環引用而產生死循環。
已標記的對象是可達的,由於應用程序代碼可經過仍在引用它的變量抵達(訪問)它。未標記的對象是不可達的,由於應用程序中不存在使對象能再次訪問的根。
CLR知道哪些對象能夠倖存,哪些能夠刪除後,就進入GC的壓縮階段。在這個階段,CLR對堆中已標記的對象進行「移動」,壓縮多有幸存下來的對象,使它們佔用連續的內存空間。最後,CLR還要從每一個根減去所引用的對象在內存中便宜的字節數。這樣就能保證麼個根仍是引用和以前同樣的對象。壓縮好內存以後,託管堆的NextObjPtr指針指向最後一個倖存對象以後的位置。下一個分配的對象將放到這個位置。
若是CLR在一次GC以後回收不了內存,並且進程中沒有空間來分配新的GC區域。就說明該進程的內存已耗盡。
靜態字段引用的對象一直存在,直到用於加載類型的AppDomain卸載爲止。會致使內存泄露,應避免使用靜態字段
1.3 垃圾回收和調試
一旦根離開做用域,它引用的對象就變得「不可達」,GC會回收其內存;不保證對象在方法的生存期中自始至終地存活。
CLR的GC是基於代的垃圾回收器:
對象越新,生存期越短。
對象越老,生存期越長。
回收堆的一部分,速度快於回收整個堆。
託管堆在初始化時不包含對象。添加到堆的對象稱爲第0代(新構造的對象)對象,垃圾回收器從未檢查過它們。
託管堆只支持三代:第0代,第1代和第2代。
若是垃圾回收器發如今回收0代後存活下來的對象不多,就可能減小第0代的預算。已分配空間的減小意味着垃圾回收將更頻繁地發生,但垃圾回收器每次作的事情也減小了,這減小了進程的工做集。
若是垃圾回收器回收了第0代,發現還有不少對象存活,沒有多少內存被回收,就會增大第0代的預算。如今,垃圾回收的次數將減小,但每次進行垃圾回收時,回收的內存要多得多。
2.1 垃圾回收出發條件
代碼顯示調用System.GC的靜態Collect方法。
Window報告低內存狀況。
CLR正在卸載AppDomain。
CLR正在關閉
2.2 大對象
85000字節或更大的對象時大對象。CLR以不一樣的方式對待大小對象
大對象不是小對象的地址空間分配,而是在進程地址空間的其餘地方分配。
目前版本的GC不壓縮大對象,由於在內存中移動它們代價太高。
大對象老是第2代,毫不多是第0代或第1代。因此只能爲須要長時間存活的資源建立大對象。大對象通常是大字符串(好比XML或JSON)或者用於I/O操做的字節數組(好比從文件或網絡將字節讀入緩衝區以便處理)。
2.3 垃圾回收模式
CLR啓動時會選擇一個GC模式,進程終止前該模式不會改變:
工做站:該模式針對客戶端應用程序優化GC。GC形成的延時很低,應用程序線程掛起時間很短,避免使用戶感到焦慮。在該模式中,GC假定機器上運行的其餘應用程序都不會消耗太多的CPU資源。
服務器:該模式針對服務器端應用程序優化GC。被優化的主要是吞吐量和資源利用。GC假定機器上沒有運行其餘應用程序,並假定機器的全部CPU均可用來輔助完成GC。該模式形成託管堆被拆分紅幾個區域,每一個CPU一個。
子模式:併發(默認) 或非併發模式。在併發模式中,垃圾回收器有一個額外的後臺線程,他能在應用程序運行時併發標記對象。
GCSetting類中GCLatencyMode屬性:
符號名稱 | 說明 |
Batch(「服務器「GC模式的默認值) | 關閉併發GC |
Interractive(「工做站」GC模式默認值) | 打開併發GC |
LowLatency | 在短時間的,時間敏感的操做中使用這個延遲模式 |
SustainedLowLatency | 使用這個延遲模式,應用程序的大多數操做都不會發生長的GC暫停 |
2.4 強制垃圾回收
GCCollectionMode枚舉
符號名稱 | 說明 |
Default | 等同於不傳遞任何符號名稱。目前還等同於傳遞Forced |
Forced | 強制回收指定的代(以及低於它的全部代) |
Optimized | 只有在能釋放大量內存或者能減小碎片化的前提下,才執行回收。0000000000000000000000000000000 |
2.5 監視應用程序的內村官使用
21.3 使用須要特殊清理的類型
包含本機資源的類型被GC時,GC會回收對象在託管堆中使用的內存。但這樣會形成本機資源的泄露。CLR提供了終結的機制,容許對象在被斷定爲垃圾以後,但在對象內存被回收以前執行一些代碼。任何包裝了本機資源(文件,網絡鏈接,套接字,互斥體)的類型都支持終結。CLR斷定一個對象不可達時,對象將終結它本身,釋放它包裝的本機資源。以後,GC會從託管堆回收對象。
CriticalFinalizerObjectl類:
首次構造任何CriticalFinalizerObject派生類型的對象時,CLR當即對繼承層次結構的全部Finalize方法進行JIT編譯。
CLR是在調用了非CriticalFinlizerObject派生類型的Finalize方法以後,才調用CriticalFinalizerObject派生類型的Finalize方法。
若是AppDomain被一個宿主應用程序強行中斷,CLR將調用CriticalFinalizerObject派生類型的Finalize方法。
3.1 使用包裝了本機資源的類型
3.2 一個有趣的依賴性問題
3.3 GC爲本機資源提供的其餘功能
3.4 終結的內部工做原理
應用程序建立新對象時,new操做符會從堆中分配內存。若是對象的類型定義了Finalize方法,那麼在該類型的實例構造器被調用以前,會將指向該對象的指針放到一個終結列表中。終結列表是由垃圾回收器控制的一個內部數據結構。列表中的每一項都指向一個對象——回收該對象的內存前應調用它的Finalize方法。
3.5 手動監視和控制對象的生存期
CLR爲每一個AppDomain都提供了一個GC句柄表,容許應用程序監視或手動控制對象的生存期。這個表在AppDomain建立之初時空白的。表中每一個記錄項包含如下兩種信息:對託管堆中的一個對象的引用,以及指出若是監視或控制對象的標識。
GCHandleType枚舉類型:
Weak:該標誌容許監視對象的生存期。
WeakTrackResurrection:該標誌容許監視對象的生存期。
Normal:該標誌容許控制對象的生存期。
Pined:該標誌容許控制對象的生存期。
垃圾回收發生時,垃圾回收器的行爲:
1.垃圾回收器標記全部可達的對象。而後,垃圾回收器掃描GC句柄表:全部Normal或Pinned對象都被當作是根,同時標記這些對象(包括這些對象經過它們的字段引用的對象)。
2.垃圾回收器掃描GC句柄表,查找全部Weak記錄項。若是一個Weak記錄項引用了未標記的對象,該引用標識的就是不可達對象,該記錄項的引用值更改成null。
3.垃圾回收器掃描終結列表。在列表中,對未標記的引用標識的是不可達對象,這些引用從終結列表移至freachable隊列。這時對象會被標記,由於對象又變成可達了。
4.垃圾回收器掃描GC句柄表,查找全部WeakTrackResurrection記錄項。
5.垃圾回收器對內存進行壓縮,填補不可達對象留下的內存」空洞「,這其實就是一個內存碎片整理的過程。Pinned對象不會壓縮(移動),垃圾回收器會移動它周圍的其餘對象。