CLR的GC工作模式介紹(Workstation和Server)

CLR的核心功能之一就是垃圾回收(garbage collection),關於GC的基本概念本文不在贅述。這裏主要針對GC的兩種工作模式展開討論和研究。

Workstaction模式介紹

該模式設計的目的是用於客戶端類的應用(Client),這類應用的部署特點是同一臺機器會部署很多應用程序,並且這些應用程序的性能要求並沒有服務器程序(nginx、asp.net等)那麼高。那麼在此種場景下,GC做了哪些設計和調整呢?

  1. 首先,最顯著的特點是當觸發垃圾回收時,CLR並沒有安排專用的線程來執行垃圾回收操作,僅僅是使用當前觸發GC的線程去執行後續操作,並且連線程優先級都沒有提升。那麼自然GC操作就要和其它用戶線程來平等競爭CPU使用。
  2. 其次,GC堆的數量上也僅僅只有1組(1個大對象堆、1個小對象堆),這樣可以更少的佔用內存空間。

Server模式介紹

顧名思義,Server模式就是針對服務器場景設計的,此類應用的部署特點是服務器內存大CPU核心多,部署的程序數量少(基本都是專機專用),但是對程序性能要求非常高。於是這種模式下,GC的也做了相應的調整:

  1. 基於CPU核心多的特點,CLR則分配專用的線程(按照CPU核心數分配)來執行GC操作,且專用線程的優先級爲THREAD_PRIORITY_HIGHEST,也比普通託管線程更高。這樣當需要執行GC時可以保證優先佔用CPU,從而儘快完成GC操作,儘快滿足內存申請所需空間。
  2. 基於內存大的特點,那就多分配GC堆,且每個堆更大,從而避免頻繁的觸發GC。具體來說,GC堆數量是根據CPU核心數來分配,比如8核CPU就分配8組GC堆。至於堆的默認大小,將按照如下規則分配:

注意上表的值是每個堆的大小,對多核CPU來說,Server模式的GC堆分配空間顯然是大於Wordstation模式的。

什麼是後臺垃圾回收(Background)?

我們知道CLR根據GC次數把對象分爲0代、1代、2代。其中01代執行的頻率高、耗時短,也被稱爲ephemeral generations。 根據CLR的設計,執行01代垃圾回收時,所有託管線程是被短暫掛起,直到垃圾回收結束後再恢復執行。但是執行2代的垃圾回收會耗時相對更長,如果也掛起全部線程,無疑會對程序響應造成較大的影響。

因此CLR提出單獨分配專用線程,用來執行2代垃圾回收,且此線程執行時不會掛起其它線程,從而降低對程序的影響。注意此處2代垃圾回收其實是包含了之前的01代的,只不過01代執行很短暫且01代執行時會依舊會掛起全部線程。

關於Background專用線程的數量,workstation模式擁有1個專用線程,server模式分配和邏輯處理器數量一致的專用線程。

在workstation模式下background回收的執行示意圖:

後臺工作站垃圾回收

上圖的解讀:

  1. GC THREAD就是background專用線程,可以看到其執行期間,其它線程沒有影響。
  2. 其中綠色箭頭代表01代的回收操作,可以看到其執行就是在普通的THREAD1線程,而且掛起了其它所有線程。

在server模式下background回收的執行示意圖:

後臺服務器垃圾回收

上圖解讀:

  1. GC THREAD1/2代表01代的專用線程,BGC THREAD1/2代表background專用線程。
  2. 同樣的background線程執行回收時,其它線程不受影響。
  3. 綠色箭頭代表01代的回收操作,可以看到掛起了其它所有線程。

需要額外說明是,background是最新的叫法,之前版本稱爲concurrent。他們之間的升級關係如下:

  workstation模式 server模式
.NET Framework 4之前 concurrent gc 不支持
.NET Framework 4 background gc 不支持
.NET Framework 4.5及之後 background gc background gc

二個模式的對比

  workstation模式 server模式 說明
專用線程 有單獨的專用線程,且數量等於CPU核心數 server模式充分利用多核特性,最大化保證快速完成GC操作。
gc執行的優先級 普通 THREAD_PRIORITY_HIGHEST workstation模式線程執行GC操作需要和普通業務線程平等競爭CPU,GC不能最快的完成。
gc堆數量 1 多個,數量等於CPU核心數

workstation模式堆數量少且內存更少,但是容易引起GC。

server模式堆數量多佔用內存更多,可以降低觸發GC的次數,進而提高程序的響應性能。

background支持情況 支持,但只會分配1個background專用線程 支持,分配的background專用線程數量等於CPU核心數

 

怎麼設置不同工作模式?

項目文件csproj中設置:

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <ServerGarbageCollection>true</ServerGarbageCollection>
    <ConcurrentGarbageCollection>true</ConcurrentGarbageCollection>
  </PropertyGroup>
</Project>

runtimeconfig.json 文件設置:

{
    "runtimeOptions":{
        "configProperties":{
            "System.GC.Server":true,
            "System.GC.Concurrent":true
        }
    }
}

單獨說明一點,對於單核CPU的計算機,無論如何配置都是workstation模式,當然現代計算機這種情況極少了。

通過dump驗證上述機制

對於workstation模式的程序進程,通過dump查看gc堆信息和gc模式信息分別如下:

0:004> !eeversion
6.0.21.52210 free
Workstation mode
SOS Version: 6.0.5.7301 retail build


0:004>  !eeheap -gc
Number of GC Heaps: 1
generation 0 starts at 0x0000024E80001030
generation 1 starts at 0x0000024E80001018
generation 2 starts at 0x0000024E80001000
ephemeral segment allocation context: none
         segment             begin         allocated         committed    allocated size    committed size
0000024E80000000  0000024E80001000  0000024E8010DFE8  0000024E80112000  0x10cfe8(1101800)  0x111000(1118208)
Large object heap starts at 0x0000024E90001000
         segment             begin         allocated         committed    allocated size    committed size
0000024E90000000  0000024E90001000  0000024E9004FD40  0000024E90050000  0x4ed40(322880)  0x4f000(323584)
Pinned object heap starts at 0x0000024E98001000
0000024E98000000  0000024E98001000  0000024E98006C50  0000024E98012000  0x5c50(23632)  0x11000(69632)
Total Allocated Size:              Size: 0x161978 (1448312) bytes.
Total Committed Size:              Size: 0x160000 (1441792) bytes.
------------------------------
GC Allocated Heap Size:    Size: 0x161978 (1448312) bytes.
GC Committed Heap Size:    Size: 0x160000 (1441792) bytes.

對於server模式的程序進程,通過dump查看gc堆信息和gc模式信息分別如下:

查看代碼
0:012> !eeversion
6.0.21.52210 free
Server mode with 8 gc heaps
SOS Version: 6.0.5.7301 retail build


0:015> !eeheap -gc
Number of GC Heaps: 8
------------------------------
Heap 0 (000001E88D3F3AD0)
generation 0 starts at 0x000001E88EF51030
generation 1 starts at 0x000001E88EF51018
generation 2 starts at 0x000001E88EF51000
ephemeral segment allocation context: none
         segment             begin         allocated         committed    allocated size    committed size
000001E88EF50000  000001E88EF51000  000001E88EF51048  000001E88EF52000  0x48(72)  0x1000(4096)
Large object heap starts at 0x000001EC8EF51000
         segment             begin         allocated         committed    allocated size    committed size
000001EC8EF50000  000001EC8EF51000  000001EC8EF51018  000001EC8EF52000  0x18(24)  0x1000(4096)
Pinned object heap starts at 0x000001ED0EF51000
000001ED0EF50000  000001ED0EF51000  000001ED0EF53010  000001ED0EF62000  0x2010(8208)  0x11000(69632)
Allocated Heap Size:       Size: 0x2070 (8304) bytes.
Committed Heap Size:       Size: 0x2000 (8192) bytes.
------------------------------
Heap 1 (000001E88D41F830)
generation 0 starts at 0x000001E90EF51030
generation 1 starts at 0x000001E90EF51018
generation 2 starts at 0x000001E90EF51000
ephemeral segment allocation context: none
         segment             begin         allocated         committed    allocated size    committed size
000001E90EF50000  000001E90EF51000  000001E90EF51048  000001E90EF52000  0x48(72)  0x1000(4096)
Large object heap starts at 0x000001EC9EF51000
         segment             begin         allocated         committed    allocated size    committed size
000001EC9EF50000  000001EC9EF51000  000001EC9EF51018  000001EC9EF52000  0x18(24)  0x1000(4096)
Pinned object heap starts at 0x000001ED1EF51000
000001ED1EF50000  000001ED1EF51000  000001ED1EF51018  000001ED1EF52000  0x18(24)  0x1000(4096)
Allocated Heap Size:       Size: 0x78 (120) bytes.
Committed Heap Size:       Size: 0x2000 (8192) bytes.
------------------------------
Heap 2 (000001E88D426EE0)
generation 0 starts at 0x000001E98EF51030
generation 1 starts at 0x000001E98EF51018
generation 2 starts at 0x000001E98EF51000
ephemeral segment allocation context: none
         segment             begin         allocated         committed    allocated size    committed size
000001E98EF50000  000001E98EF51000  000001E98EF51048  000001E98EF52000  0x48(72)  0x1000(4096)
Large object heap starts at 0x000001ECAEF51000
         segment             begin         allocated         committed    allocated size    committed size
000001ECAEF50000  000001ECAEF51000  000001ECAEF51018  000001ECAEF52000  0x18(24)  0x1000(4096)
Pinned object heap starts at 0x000001ED2EF51000
000001ED2EF50000  000001ED2EF51000  000001ED2EF51430  000001ED2EF52000  0x430(1072)  0x1000(4096)
Allocated Heap Size:       Size: 0x490 (1168) bytes.
Committed Heap Size:       Size: 0x2000 (8192) bytes.
------------------------------
Heap 3 (000001ED9B54AFB0)
generation 0 starts at 0x000001EA0EF51030
generation 1 starts at 0x000001EA0EF51018
generation 2 starts at 0x000001EA0EF51000
ephemeral segment allocation context: none
         segment             begin         allocated         committed    allocated size    committed size
000001EA0EF50000  000001EA0EF51000  000001EA0EF51048  000001EA0EF52000  0x48(72)  0x1000(4096)
Large object heap starts at 0x000001ECBEF51000
         segment             begin         allocated         committed    allocated size    committed size
000001ECBEF50000  000001ECBEF51000  000001ECBEF51018  000001ECBEF52000  0x18(24)  0x1000(4096)
Pinned object heap starts at 0x000001ED3EF51000
000001ED3EF50000  000001ED3EF51000  000001ED3EF51018  000001ED3EF52000  0x18(24)  0x1000(4096)
Allocated Heap Size:       Size: 0x78 (120) bytes.
Committed Heap Size:       Size: 0x2000 (8192) bytes.
------------------------------
Heap 4 (000001ED9B576570)
generation 0 starts at 0x000001EA8EF51030
generation 1 starts at 0x000001EA8EF51018
generation 2 starts at 0x000001EA8EF51000
ephemeral segment allocation context: none
         segment             begin         allocated         committed    allocated size    committed size
000001EA8EF50000  000001EA8EF51000  000001EA8EF51048  000001EA8EF52000  0x48(72)  0x1000(4096)
Large object heap starts at 0x000001ECCEF51000
         segment             begin         allocated         committed    allocated size    committed size
000001ECCEF50000  000001ECCEF51000  000001ECCEF51018  000001ECCEF52000  0x18(24)  0x1000(4096)
Pinned object heap starts at 0x000001ED4EF51000
000001ED4EF50000  000001ED4EF51000  000001ED4EF51018  000001ED4EF52000  0x18(24)  0x1000(4096)
Allocated Heap Size:       Size: 0x78 (120) bytes.
Committed Heap Size:       Size: 0x2000 (8192) bytes.
------------------------------
Heap 5 (000001ED9B5A2420)
generation 0 starts at 0x000001EB0EF51030
generation 1 starts at 0x000001EB0EF51018
generation 2 starts at 0x000001EB0EF51000
ephemeral segment allocation context: none
         segment             begin         allocated         committed    allocated size    committed size
000001EB0EF50000  000001EB0EF51000  000001EB0EF673D8  000001EB0EF72000  0x163d8(91096)  0x21000(135168)
Large object heap starts at 0x000001ECDEF51000
         segment             begin         allocated         committed    allocated size    committed size
000001ECDEF50000  000001ECDEF51000  000001ECDEF51018  000001ECDEF52000  0x18(24)  0x1000(4096)
Pinned object heap starts at 0x000001ED5EF51000
000001ED5EF50000  000001ED5EF51000  000001ED5EF53010  000001ED5EF62000  0x2010(8208)  0x11000(69632)
Allocated Heap Size:       Size: 0x18400 (99328) bytes.
Committed Heap Size:       Size: 0x22000 (139264) bytes.
------------------------------
Heap 6 (000001ED9B5CE2D0)
generation 0 starts at 0x000001EB8EF51030
generation 1 starts at 0x000001EB8EF51018
generation 2 starts at 0x000001EB8EF51000
ephemeral segment allocation context: none
         segment             begin         allocated         committed    allocated size    committed size
000001EB8EF50000  000001EB8EF51000  000001EB8EF51048  000001EB8EF52000  0x48(72)  0x1000(4096)
Large object heap starts at 0x000001ECEEF51000
         segment             begin         allocated         committed    allocated size    committed size
000001ECEEF50000  000001ECEEF51000  000001ECEEF51018  000001ECEEF52000  0x18(24)  0x1000(4096)
Pinned object heap starts at 0x000001ED6EF51000
000001ED6EF50000  000001ED6EF51000  000001ED6EF51018  000001ED6EF52000  0x18(24)  0x1000(4096)
Allocated Heap Size:       Size: 0x78 (120) bytes.
Committed Heap Size:       Size: 0x2000 (8192) bytes.
------------------------------
Heap 7 (000001ED9B5FA180)
generation 0 starts at 0x000001EC0EF51030
generation 1 starts at 0x000001EC0EF51018
generation 2 starts at 0x000001EC0EF51000
ephemeral segment allocation context: none
         segment             begin         allocated         committed    allocated size    committed size
000001EC0EF50000  000001EC0EF51000  000001EC0EF51048  000001EC0EF52000  0x48(72)  0x1000(4096)
Large object heap starts at 0x000001ECFEF51000
         segment             begin         allocated         committed    allocated size    committed size
000001ECFEF50000  000001ECFEF51000  000001ECFEF51018  000001ECFEF52000  0x18(24)  0x1000(4096)
Pinned object heap starts at 0x000001ED7EF51000
000001ED7EF50000  000001ED7EF51000  000001ED7EF51018  000001ED7EF52000  0x18(24)  0x1000(4096)
Allocated Heap Size:       Size: 0x78 (120) bytes.
Committed Heap Size:       Size: 0x2000 (8192) bytes.
------------------------------
GC Allocated Heap Size:    Size: 0x1ab58 (109400) bytes.
GC Committed Heap Size:    Size: 0x30000 (196608) bytes.

總結

 本文主要參閱了微軟官方文檔,對GC的工作模式進行了探究和驗證。可以看到,微軟設計這2個GC工作模式的目的是爲了.net應用程序在不同場景下有好的程序表現,並沒有孰優孰劣的問題。這個功能特點可以類比現在自動擋汽車的工作模式有節能模式、道路模式、雪地模式、沙漠模式一樣。因此,在實際開發程序時,對普通客戶端應用如winfrom一般選擇workstation模式,對於服務器程序如asp.net 一般選擇server模式。

參考資料

  1. 垃圾回收的基本知識 | Microsoft Docs
  2. Workstation vs. server garbage collection (GC) | Microsoft Docs
  3. 後臺垃圾回收 | Microsoft Docs
  4. 垃圾回收器配置設置 - .NET | Microsoft Docs
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章