.Net Core 中GC的工作原理
阿新 • • 發佈:2020-06-23
## **前言**
.NET 中GC管理你服務的記憶體分配和釋放,GC是執行公共語言執行時(CLR Common Language Runtime)中,GC可以幫助開發人員有效的分配記憶體和和釋放記憶體,大多數情況下是不需要去擔心的,但是有時候服務總是是出現莫名的問題,所以還是有必要了解一下GC的基礎知識的。這裡就不介紹記憶體方面的知識了。
## **GC回收過程**
`GC`將物件分為大物件和小物件,如果物件的大小大於或者等於`85000byte`將被視為大物件,大物件會被分配到到`(LOH) Large Object Heap`中去。
`GC`有一個代數的概念`Generation`,分為三代
![20200623172623](https://linhuicnblogs.oss-cn-beijing.aliyuncs.com/20200623172623.png)
* `Generation 0`: 0代,這裡面都是生命週期很短的物件,比如臨時變數,當你new一個物件的時候該物件都會在`Generation 0`中,這裡的物件將很快的被GC回收,但是當你new的是一個大物件的時候它會直接進去大物件堆(LOH)
* `Generation 1`: 1代,這一代包含的也基本是生命週期很短的物件。它是短期物件和長期物件之間的緩衝區。
* `Generation 2`: 2代,這一代包含的都是生命週期長的物件,它們都是從1代和2代中選拔出來的,`LOH`屬於2代。
當分配的物件使用的記憶體超出了`GC`的閾值時回收就會開始。閾值是隨著服務的執行`GC`自己調整的。或者直接呼叫` GC.Collect `方法也可以開始回收。
回收開始時`GC`會開始迴圈遍歷`Generation 0`中的所有物件並標記所有物件是活動物件還是非活動物件,標記完成後會更新活動物件的引用。最後會回收非活動物件佔用的記憶體,並把活動物件壓縮後移動到`Generation 1`中,`Generation 1`中的或物件在移動到`Generation 2`是預設不會被壓縮的,因為複製大的物件會導致效能的下降。可以通過`GCSettings.LargeObjectHeapCompactionMode`來配置壓縮`LOH`。
## **GC的回收型別**
GC 回收有兩種型別,`WorkStation GC`(工作站)和`Server GC`(伺服器),.Net Core服務預設情況下時使用`WorkStation GC`工作站模式來回收。
* `Server GC`會擁有更大的記憶體,`Server GC`會為每個處理器建立一個用於執行垃圾回收的堆和專用執行緒,每個堆都擁有一個小物件堆和大物件堆,並且所有的堆都可以訪問。 不同堆上的物件可以相互引用。因為多個垃圾回收執行緒一起工作,所以對於相同大小的堆`Server GC`垃圾回收比`WorkStation GC`垃圾回收更快一些。但是`Server GC`回收會佔用大量資源,這種模式的特點是初始分配的記憶體較大,並且儘可能不回收記憶體,進行回收用時會很耗時,並進行記憶體碎片整理工作。
* `Workstation GC`的記憶體相對於`Server GC`就很小啦,且它的回收執行緒就是服務的執行緒且有較高的優先順序,因為必須與其他執行緒競爭 CPU 時間來進行回收。
不同模式下的記憶體分配
![20200623154852](https://linhuicnblogs.oss-cn-beijing.aliyuncs.com/20200623154852.png)
## **GC的回收模式**
`GC`有三種回收模式
* `Non-Concurrent GC` 非並行回收模式:在非並行模式下,回收時候會掛起所有其他的執行緒影響服務的效能。
* `Concurrent GC` 並行回收模式: 並行會後可以解決非並行回收引起的執行緒掛起,讓其他執行緒和回收執行緒一起執行,使服務可以更快的響應,並行回收只會發生在`Generation 2`中,`Generation 0/1`始終都是非併發的,因為他們都是小物件回收的速度很快。在並行回收的時候我們依舊可以分配物件到`Generation 0/1`中。
* `Background GC` 後臺回收模式:`Background GC` 是 `Concurrent GC`的增強版本。 區別在`Background GC`回收`Generation 2`的時允許了`Generation 0/1` 進行清理。在`WorkStation GC`下會使用一個專用的後臺垃圾回收執行緒,而`Server GC`下會使用多個執行緒來進行回收。且`Server GC`下回收執行緒不會超時。
非並行回收:
![20200623143747](https://linhuicnblogs.oss-cn-beijing.aliyuncs.com/20200623143747.png)
並行回收
![20200623170251](https://linhuicnblogs.oss-cn-beijing.aliyuncs.com/20200623170251.png)
WorkStation GC 後臺回收
![20200623145944](https://linhuicnblogs.oss-cn-beijing.aliyuncs.com/20200623145944.png)
Server GC 後臺回收
![20200623150013](https://linhuicnblogs.oss-cn-beijing.aliyuncs.com/20200623150013.png)
## **GC回收型別配置**
推薦使用`runtimeconfig.json`檔案和環境變數`COMPlus_gcServer`來配置。
`COMPlus_gcServer` 0 = `WorkStation GC`
`COMPlus_gcServer` 1 = `Server GC`
```json
{
"runtimeOptions": {
"configProperties": {
"System.GC.Server": true
//true - Server GC false - WorkStation GC
}
}
}
```
## **GC回收模式配置**
推薦使用`runtimeconfig.json`檔案和環境變數`COMPlus_gcConcurrent`來配置。
`COMPlus_gcConcurrent` 0 =Non-Concurrent GC
`COMPlus_gcConcurrent` 1 =Background GC
```json
{
"runtimeOptions": {
"configProperties": {
"System.GC.Concurrent": true
//true- Background GC false -Non-Concurrent GC
}
}
}
```
## **強制回收**
在一些特殊的情況下強制回收是可以提高服務的效能的,可以向`GC.Collect()`提供` GCCollectionMode `列舉值觸發強制回收。
* Default :預設的回收設定。
* Forced :立即強制進行垃圾回收。
* Optimized : `GC`來判斷時間是否是回收物件的最佳時間,如`GC`判定回收效率不高因此回收不合理的情況下將返回不回收物件。
```csharp
GC.Collect( (int) GCCollectionMode.Forced);
```
## **延遲迴收**
在我們的服務在檢索資料或者處理邏輯的時候可能會發生垃圾回收,從而妨礙效能,可以通過` System.Runtime.GCLatencyMode `來配置延遲迴收
* GCLatencyMode.LowLatency:禁止`Generation 2`回收,只回收`Generation 0/1`,這個只能在短時間內使用,如果長時間使用記憶體處於壓力下`GC`還是會觸發回收,這個配置只對`WorkStation GC`可用。
* GCLatencyMode.SustainedLowLatency :禁止`Generation 2`的` Foreground GC `(前臺回收),只回收`Generation 0/1`和`Generation 2`後臺回收。`WorkStation GC`和`Server GC`都可以使用,且可以長時間使用,但是如果禁用`Background GC`,將無法使用。
```csharp
GC.Collect( (int) GCLatencyMode.SustainedLowLatency);
```
## **參考文章**
[從ASP.NET Core 3.0 preview 特性,瞭解CLR的Garbage Collection](https://www.cnblogs.com/dacc123/p/10980718.html)
[微軟文件](https://docs.microsoft.com/zh-cn/dotnet/standard/garbage-collection/fundamentals)
## **總結**
參考了一些大佬和官方的文件簡單的去了解了一下GC的工作原理,方便在開發中有效區分配使用記憶體資源,文中如有錯誤大佬們可以在評論區指出。