工作站模式和服务器模式 以及并发GC
1.背景:
.NET Framework 的 CLR(公共语言运行时,Common Language Runtime)的两种不同构建版本:Workstation(工作站版本) 和 Server(服务器版本)。这两个版本在底层实现上存在差异,主要针对不同的应用场景进行优化。
在早期 .NET Framework(如 2.0-3.5)中,CLR 的宿主(Hosting)机制通过不同的非托管代码模块实现,具体表现为:
-
mscorwks.dll(Microsoft Common Object Runtime WorkStation):工作站版本的 CLR 核心模块。 -
mscorsvr.dll(Microsoft Common Object Runtime Server):服务器版本的 CLR 核心模块。
这些模块由 C++ 编写,属于非托管代码,负责 CLR 的启动、内存管理(GC)、线程调度等核心功能
1.非托管入口点
-
CLR 的启动由非托管代码(C++)实现,入口模块为
mscoree.dll(Microsoft Component Object Runtime Execution Engine)。 -
mscoree.dll负责加载mscorwks.dll或mscorsvr.dll,具体取决于配置。
1.显示配置
通过配置文件(app.config)指定
<configuration>
<runtime>
<gcServer enabled="true"/> <!-- 启用 Server GC -->
<gcConcurrent enabled="true"/> <!-- 启用 Concurrent GC(仅Workstation) -->
</runtime>
</configuration>
-
或通过环境变量
COMPLUS_gcServer设置为1。
2.隐式推断:
若未显式配置,CLR 根据进程类型自动选择:
控制台应用、WinForms/WPF 默认使用 Workstation。
ASP.NET、Windows 服务等默认使用 Server(取决于宿主配置)。
3.NET Framework 4.0 及之后:
-
-
mscorwks.dll和mscorsvr.dll合并为clr.dll,但两种 GC 模式(Workstation/Server)仍然保留。 -
通过配置选择 GC 模式,而非直接依赖不同的 DLL。
-
4.NET Core / .NET 5+:
完全重构的运行时(CoreCLR/CoreRT),不再区分 Workstation/Server 的 DLL。
但 GC 模式(Workstation/Server)仍然存在,可通过配置选择:
2.目标场景
-
Workstation(工作站版本):
-
优化目标:交互性(低延迟)和 单线程性能。
-
适用场景:客户端应用程序(如桌面应用、WinForms/WPF)、需要快速响应的场景。
-
-
Server(服务器版本):
-
优化目标:高吞吐量 和 多线程并行性。
-
适用场景:服务器端应用程序(如 ASP.NET、后台服务)、多核 CPU 密集型任务。
-
Workstation GC:提供了一种默认的并发GC模式(当然这个是可以选择关掉)
-
并发 GC(Concurrent GC):允许用户线程与 GC 线程交替执行,减少暂停时间。
-
适合需要低延迟的交互式应用,避免界面卡顿
Server GC:
-
多堆并行回收:
-
为每个逻辑 CPU 核心分配独立的堆(Heap),减少线程竞争。
-
使用专用 GC 线程(每个 CPU 核心一个线程),并行回收垃圾。
-
-
适合高吞吐量的多线程服务,最大化 CPU 利用率。
线程池调度
-
Workstation:
-
线程池的线程创建策略更保守,避免过度占用资源。
-
-
Server:
-
线程池初始分配更多线程(与 CPU 核心数相关),适应高并发请求。
-
3.分代GC:内存管理的基础结构
1.核心设计
分代模型是 .NET GC 的核心设计,基于以下假设:
-
弱分代假设(Weak Generational Hypothesis):大多数对象生命周期短暂,存活时间较短。
-
强分代假设(Strong Generational Hypothesis):存活时间越长的对象,越不容易被回收。
因此,.NET 将堆内存划分为三个代:
-
第0代(Gen 0):存放最新创建的对象。
-
第1代(Gen 1):存放从 Gen 0 晋升的存活对象。
-
第2代(Gen 2):存放从 Gen 1 晋升的长生命周期对象。
-
新对象分配:所有新对象初始分配在 Gen 0。
-
Gen 0 回收:
-
当 Gen 0 满时触发回收。
-
存活的对象晋升到 Gen 1。
-
回收快速且频率高(通常仅需几毫秒)。
-
-
Gen 1 回收:
-
若 Gen 0 回收后 Gen 1 仍满,触发 Gen 1 回收。
-
存活对象晋升到 Gen 2。
-
-
Gen 2 回收:
-
当 Gen 2 满时触发,回收耗时较长(可能数十到数百毫秒)。
-
存活对象保留在 Gen 2。
-
分代模型的优势在于减少扫描范围,避免每次回收都遍历整个堆
4.并发GC:减少暂停时间的执行策略
并发 GC 是分代模型的一种补充优化,目标是最小化应用程序线程的暂停时间(Stop-The-World, STW)。
其核心思想是:在 GC 回收过程中,允许应用程序线程继续执行。
1.并发GC的具体实现
在 .NET 的 Workstation GC(工作站模式) 中:
-
仅作用于 Gen 2 的回收:
-
Gen 0/1 的回收时间极短,直接采用 STW 暂停。
-
Gen 2 的回收耗时较长,因此使用并发模式。
-
-
并发阶段流程:
-
初始标记(Initial Marking):短暂暂停,标记根对象。
-
并发标记(Concurrent Marking):应用程序线程与 GC 线程并发执行。
-
重新标记(Final Marking):短暂暂停,修正并发期间对象状态变化。
-
并发清理(Concurrent Sweep):回收不可达内存,应用程序线程继续运行。
-
2.并发GC的局限性
-
内存碎片:并发清理可能导致内存碎片化。
-
CPU 资源竞争:GC 线程与应用程序线程共享 CPU 资源。
3.可以关闭并发GC
| 行为 | 并发 GC(默认) | 非并发 GC(禁用并发) |
|---|---|---|
| Gen 2 回收暂停时间 | 较短(分阶段暂停) | 较长(完全暂停) |
| CPU 资源占用 | GC 线程与应用线程共享 CPU | GC 独占 CPU,回收更快完成 |
| 适用场景 | 交互式应用(如 UI 程序) | 批处理任务、后台服务(允许较长暂停) |
需要说明的是:NET5中是这样设
/ 设置延迟模式为 Batch(禁用并发 GC)
GCSettings.LatencyMode = GCLatencyMode.Batch;
禁用并发 GC 的适用场景
a. 高吞吐批处理任务
例如数据转换、离线计算等任务,允许完全暂停以换取更快的 GC 完成速度。
优势:减少 GC 线程与应用线程的 CPU 竞争,提高整体吞吐量。
b. 内存密集型后台服务
若服务对延迟不敏感,但需要快速释放内存。
示例:日志处理服务、文件压缩服务。
c. 调试与分析
禁用并发 GC 可以使 GC 行为更可预测,便于调试内存问题。
5.服务器模式 GC 的核心设计
服务器模式 GC 的底层逻辑是:
-
为每个逻辑 CPU 核心分配独立的堆(Heap),每个堆包含 Gen 0、Gen 1 和 Gen 2。
-
为每个堆分配专用的 GC 线程,并行执行垃圾回收。
-
共享大对象堆(LOH, Large Object Heap),所有堆共享同一个 LOH。
在服务器模式下,Gen 2 的回收是并行的,但实现方式与 Gen 0/1 不同
-
全局触发:当任意堆的 Gen 2 满时,触发全局 Gen 2 回收。
-
多线程协同:
-
分段标记(Segment Marking):将 Gen 2 堆划分为多个逻辑段,每个 GC 线程负责一个段的标记。
-
并行压缩(Parallel Compaction):多个线程并行移动存活对象,减少内存碎片。
-
-
完全暂停(Stop-The-World):
-
Gen 2 回收期间,所有应用程序线程暂停。
-
但 GC 线程之间并行工作,最大化利用多核 CPU。
-
6.总结:
1. 工作站模式(Workstation GC)
a. Gen 0/1 回收
-
STW(完全暂停):无论是否启用并发 GC,Gen 0/1 的回收均会完全暂停用户线程。
-
原因:Gen 0/1 回收极快(通常 <1ms),直接暂停线程对用户体验影响极小。
-
开启并发 GC(默认):
-
分阶段暂停:Gen 2 回收分为多个阶段(初始标记、并发标记、重新标记、并发清理)。
-
用户线程与 GC 线程交替执行:仅在初始标记和重新标记阶段短暂暂停用户线程,其他阶段允许用户线程继续运行。
-
目标:减少长时间暂停,适合交互式应用(如 UI 程序)。
-
-
关闭并发 GC:
-
完全 STW:Gen 2 回收期间完全暂停用户线程,GC 线程独占 CPU 执行。
-
目标:以更高的暂停时间换取更快的回收速度,适合批处理任务。
-
2. 服务器模式(Server GC)
a. Gen 0/1 回收
-
独立堆与并行回收:
-
每个逻辑 CPU 核心分配独立的堆(包含 Gen 0/1/2 段)。
-
当某个堆的 Gen 0/1 满时,仅触发该堆的回收,其他堆不受影响。
-
并行处理:每个堆的 GC 线程独立工作,无跨堆协作。
-
STW 暂停:但暂停时间极短(因仅处理单个堆)。
-
b. Gen 2 回收
-
全局触发与完全 STW:
-
当任意堆的 Gen 2 满时,触发全局 Gen 2 回收。
-
完全暂停用户线程:所有应用程序线程停止。
-
-
多线程并行回收:
-
多个 GC 线程协同处理所有堆的 Gen 2 段。
-
分段处理:每个线程负责标记和压缩堆的一部分(逻辑分段)。
-
大对象堆(LOH)共享:所有堆共享同一个 LOH,回收时由多个线程协同处理。
-
c. 关键差异
-
并行性:服务器模式的 Gen 2 回收是多线程并行处理,但用户线程完全暂停。
-
吞吐量优化:通过多核并行缩短总回收时间,适合高吞吐场景(如后端服务)。
3. 对比总结
| 特性 | 工作站模式(并发 GC 开启) | 工作站模式(并发 GC 关闭) | 服务器模式 |
|---|---|---|---|
| Gen 0/1 回收 | STW(极短暂停) | STW(极短暂停) | STW(极短暂停,各堆独立并行) |
| Gen 2 回收策略 | 分阶段暂停(用户线程交替执行) | 完全 STW | 完全 STW,多线程并行处理 |
| 目标场景 | 低延迟交互式应用(如桌面程序) | 批处理任务 | 高吞吐服务器应用(如 ASP.NET) |
| 内存占用 | 单个堆 | 单个堆 | 每个 CPU 核心一个堆 |
| LOH 处理 | 共享 | 共享 | 共享,但并行回收 |
4. 配置建议
-
工作站模式:
-
默认启用并发 GC(
<gcConcurrent enabled="true"/>)。 -
若需优化吞吐量(允许较长暂停),可禁用并发 GC。
-
-
服务器模式:
-
始终通过
<gcServer enabled="true"/>启用。 -
在多核服务器环境中性能优势显著。
-
浙公网安备 33010602011771号