CLR的GC工作模式介绍(Workstation和Server)
CLR的核心功能之一就是垃圾回收(garbage collection),关于GC的基本概念本文不在赘述。这里主要针对GC的两种工作模式展开讨论和研究。
Workstaction模式介绍
该模式设计的目的是用于客户端类的应用(Client),这类应用的部署特点是同一台机器会部署很多应用程序,并且这些应用程序的性能要求并没有服务器程序(nginx、asp.net等)那么高。那么在此种场景下,GC做了哪些设计和调整呢?
- 首先,最显著的特点是当触发垃圾回收时,CLR并没有安排专用的线程来执行垃圾回收操作,仅仅是使用当前触发GC的线程去执行后续操作,并且连线程优先级都没有提升。那么自然GC操作就要和其它用户线程来平等竞争CPU使用。
- 其次,GC堆的数量上也仅仅只有1组(1个大对象堆、1个小对象堆),这样可以更少的占用内存空间。
Server模式介绍
顾名思义,Server模式就是针对服务器场景设计的,此类应用的部署特点是服务器内存大CPU核心多,部署的程序数量少(基本都是专机专用),但是对程序性能要求非常高。于是这种模式下,GC的也做了相应的调整:
- 基于CPU核心多的特点,CLR则分配专用的线程(按照CPU核心数分配)来执行GC操作,且专用线程的优先级为THREAD_PRIORITY_HIGHEST,也比普通托管线程更高。这样当需要执行GC时可以保证优先占用CPU,从而尽快完成GC操作,尽快满足内存申请所需空间。
- 基于内存大的特点,那就多分配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回收的执行示意图:
上图的解读:
- GC THREAD就是background专用线程,可以看到其执行期间,其它线程没有影响。
- 其中绿色箭头代表01代的回收操作,可以看到其执行就是在普通的THREAD1线程,而且挂起了其它所有线程。
在server模式下background回收的执行示意图:
上图解读:
- GC THREAD1/2代表01代的专用线程,BGC THREAD1/2代表background专用线程。
- 同样的background线程执行回收时,其它线程不受影响。
- 绿色箭头代表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模式。