簡介
GC管理你服務的內存分配和釋放,GC是運行公共語言運行時(CLR Common Language Runtime)中,GC可以幫助開發人員有效的分配內存和和釋放內存,大多數情況下是不需要去擔心的,但是有時候服務總是是出現莫名的問題,所以還是有必要了解一下GC的基礎知識的。
1、GC調優本質是什么?
GC調優本質是針對堆對象的回收及優化,把控程序運行狀態,在有限的硬件資源中穩定運行下去,不讓它造成內存溢出。
2、GC調優指標是什么?
暫停時間是GC很重要的一個指標,意思是在GC暫停多長時間才能執行其它工作(常說的"卡頓" 或 "掛起線程"),暫停時間較長就會直接影響程序工作延遲。。。(如果是大型項目至於會造成什么影響,腦補下。。。)
注:如果線程當前正在運行時執行C / C ++代碼,則GC可能需要等到該調用完成后才能掛起線程。因此,我們盡量使用托管代碼而不是本機代碼,GC暫停時間就越好。
CLR
.NET程序是運行在 CLR : Common Language Runtime 之上。CLR 就像 JAVA 中的 JVM 虛擬機。CLR 包括了JIT編譯器,GC(垃圾回收器),CIL(公共中間語言) CLI(公共語言基礎架構)。
概念:
1、我們的程序都是在操作虛擬內存地址,從來不直接操作內存地址,即使是 Native Code。(注:虛擬內存在win中叫"虛擬內存" linux中叫"交換空間")
2、一個進程會被分配一個獨立的虛擬內存空間,我們定義的和管理的對象都在這些空間之中。
3、虛擬內存空間中的內存 有三種狀態:空閑 (可以隨時分配對象),預定 (被某個進程預定,尚且不能分配對象),提交(從物理內存中分配了地址到該虛擬內存,這個時候才可以分配對象)
4、CLR初始化GC 后,GC 就在上面說的虛擬內存空間中分配內存,用來讓它管理和分配對象,被分配的內存叫做 Managed Heap 管理堆,每個進程都有一個管理堆內存,進程中的線程共享一個管理堆內存
5、CLR中還有一塊堆內存叫做LOH Large Object Heap 。它也是隸屬於 GC 管理,但是它很特別,只分配大於 85000byte 的對象,所以叫做大對象,為什么要這么做呢?很顯然大對象太難管理了,GC 回收大對象將很耗時,所以沒辦法,只有給這些 “大象” 另選一出房子,GC 這個“管理員” 很少管 “大象”。
那么什么時候對象會被分配到堆內存中呢?
所有引用類型的對象,以及作為類屬性的值類型對象,都會分配在堆中。大於 85000byte 的對象扔到 “大象房” 里。
堆內存中的對象越少,GC 干的事情越少,你的程序就越快,因為 GC 在干事的時候,程序中的其他線程都必須畢恭畢敬的站着不動(掛起),等 GC 說:我已經清理好了。然后大家才開始繼續忙碌。所以 GC 一直都是在干幫線程擦屁股的事情。
所以沒有 GC 的編程語言更快,但是也更容易產生廢物。
GC Generation
那么 GC 在收拾垃圾的過程中到底做了什么呢?首先要了解 CLR 的 GC 有一個Generation "代" 的概念 GC 通過將對象分為三代,優化對象管理。GC 中的代分為三代:
1、Generation0 零代或者叫做初代,初代中都是一些短命的對象,shorter object,它們通常會被很快清除。當 new 一個新對象的時候,該對象都會分配在 Generation 0 中。只有一段連續的內存
2、Generation1 一代,一代中的對象也是短命對象,它相當於 shorter object 和 longer object 之間的緩沖區。只有一段連續的內存
3、Generation2 二代,二代中的對象都是長壽對象,他們都是從零代和一代中選拔而來,一旦進入二代,那就意味着你很安全。之前說的 LOH 就屬於二代,static 定義的對象也是直接分配在二代中。包含多段連續的內存。
GC 回收類型
GC 有兩種形式:WorkStation GC和 Server GC 默認的.NET程序都是WorkStation GC
WorkStation GC 和 Server GC區別:
1、Server GC 的 Generation 內存更大,64位操作系統 Generation 0 的大小居然有4G ,這意味着啥?在不調用GC.Collect
的情況下,4G 塞滿GC 才會去回收。那樣性能可是有很大的提升。但是一旦回收了,4GB 的“垃圾” 也夠GC喝一壺的了。
2、Server GC 擁有專門用來處理 GC的線程,而WorkStation GC 的處理線程就是你的應用程序線程。WorkStation 形式下,GC 開始,所有應用程序線程掛起,GC選擇最后一個應用程序線程用來跑GC,直到GC 完成。所有線程恢復。而ServerGC 形式下: 有幾核CPU ,那么就有幾個專有的線程來處理 GC。每個線程都一個堆進行GC ,不同的堆的對象可以相互引用。所以在GC 的過程中,Server GC 比 WorkStation GC 更快。有專有線程,但並不代表可以並行GC哦。
上面兩個區別,決定了 Server GC 用於對付高吞吐量的程序,而WorkStation GC 用於一般的客戶端程序足以。
零代和一代 占用的內存因為他們都是短暫對象,所以叫做短暫內存塊。 那么他們占用的內存大小是多大?32位和63位的系統是不一樣的,不同的GC類型也是不一樣的。
注:ServerGC開啟后是根據服務器CPU核心數來創建多個GC 而不是CPU物理數
GC回收模式
GC 有三種模式:
1、Non-Concurrent 非並行回收模式:在非並行模式下,回收時候會掛起所有其他的線程影響服務的性能。
2、Concurrent GC 並行回收模式:並行會后可以解決非並行回收引起的線程掛起,讓其他線程和回收線程一起運行,使服務可以更快的響應,並行回收只會發生在Generation 2中,Generation 0/1始終都是非並發的,因為他們都是小對象回收的速度很快。在並行回收的時候依舊可以分配對象到Generation 0/1中。
3、Background GC 后台回收模式:Background GC 是 Concurrent GC的增強版本。
Background GC 和 Concurrent GC 的區別:
首先:Background GC 和 Concurrent GC 都是為了減少 因為 GC 而掛起工作線程的時間,從而提升用戶交互體驗,程序響應速度。
其次:Background GC 和 Concurrent GC 一樣,都是使用一個專有的GC 線程,並且都是在 Generation 2 中起作用。
最后:Background GC 是 Concurrent GC 的增強版,在.NET 4.0 之前都是默認使用 Concurrent GC 而 .NET 4.0+ 之后使用Background GC 代替了 Concurrent GC。
Background GC 比 Concurrent GC 多了什么:
上面說到 Concurrent GC 在 Generation 2 中進行清理時,工作線程仍然可以在 Generation 0/1 中進行分配對象,但是這是有限制的,當 Generation 0/1 中的內存片段 Segment 用完的時候,就不能再分配了,直到 Concurrent GC 完成。
而 Background GC 沒有這個限制,因為 Background GC 在 Generation 2 中進行清理時,允許 Generation 0/1 進行清理,也就說是當 Generation 0/1 的 Segment 用完的時候,GC 可以去清理它們,這個GC 稱作 Foreground GC ( 前台GC ) ,Foreground GC 清理完之后,工作線程就可以繼續分配對象了。
Background GC回收Generation 2的時允許了Generation 0/1 進行清理。
在WorkStation GC下會使用一個專用的后台垃圾回收線程,而Server GC下會使用多個線程來進行回收。
且Server GC下回收線程不會超時。
所以 Background GC 比 Concurrent GC 減少了更多 工作線程暫停的時間。
總結:
自從.NET Core 3.0開始對根據自己具體的應用場景去配置GC ,讓GC 發揮最好的作用。比如啟用Server GC 對於高吞吐量的程序有幫助,比如禁用 Concurrent GC 實際上對一個高密度計算的程序是有性能提升的。
.NET 5 改動更大,而且.NET 5整體性能比.net core 3.1高20%,並且在GC這塊.NET 5開放了更多配置,所以.NET 5很值得關注。
參考:
1、.NET 5中的性能改進
https://devblogs.microsoft.com/dotnet/performance-improvements-in-net-5/
2、垃圾收集的基本原理
https://docs.microsoft.com/en-us/dotnet/standard/garbage-collection/fundamentals
3、垃圾收集的運行時配置選項 (注:這里面就有.net 版本對比與.NET5新開放的配置)
https://docs.microsoft.com/en-us/dotnet/core/run-time-config/garbage-collector
4、使用並發可視化工具了解不同的GC模式
5、小型容器使用Server GC
https://devblogs.microsoft.com/dotnet/running-with-server-gc-in-a-small-container-scenario-part-0/
6、.NET3新功能
https://devblogs.microsoft.com/dotnet/announcing-net-core-3-preview-3/