内存分配
栈空间和堆空间
栈空间用于存储值类型变量、方法参数等,由系统自动分配释放,无需GC介入;堆空间用于存储引用类型对象,由GC统一管理。new关键字创建的对象实际分配在托管堆中,栈上仅保存该对象的引用地址。
值类型和引用类型
所有值类型都隐式继承自System.ValueType,而System.ValueType本身又继承自System.Object,值类型直接存储数据本身,分配在栈上;引用类型存储的是指向堆中数据的地址,分配在托管堆中。
cs
// 值类型示例
int a = 10; // 分配在栈上
// 引用类型示例
var person = new { Name = "张三", Age = 20 }; // 实例分配在托管堆,栈上仅存引用
创建一个新对象
.NET Core采用两种分配器:
- 线性分配器:通过移动NextObjPtr指针直接分配内存,速度极快,是小对象堆的默认分配方式。
- 空闲链表分配器:维护内存中空闲块的链表,当线性分配器没有足够连续空间时,从链表中查找匹配的空闲块进行分配,主要用于大对象堆和固定对象堆。
小对象堆分配
小对象堆(SOH)专门存放小于85000字节的对象,内部划分为Gen 0、Gen 1、Gen 2三个代,分配时直接移动指针,GC回收时会自动压缩内存消除碎片。
大对象堆分配
大于等于85000字节的对象直接进入大对象堆(LOH),逻辑上属于Gen 2的一部分,仅在Full GC时才会被回收,默认不执行内存压缩,容易产生内存碎片,可通过GCSettings.LargeObjectHeapCompactionMode手动触发压缩。
固定对象堆分配
固定对象堆(POH)专门存放需要被固定(不能被GC移动)的对象,主要用于和非托管代码交互的场景,避免GC移动对象导致非托管指针失效,仅使用空闲链表分配器进行内存管理。
垃圾回收器 图解和过程
分代
GC基于"大部分对象生命周期很短"的假设,将托管堆划分为3代:Gen 0(新创建的短生命周期对象,回收频率最高)、Gen 1(中间缓冲代)、Gen 2(长生命周期对象,回收频率最低),大对象堆逻辑上属于Gen 2。
标记阶段
GC首先暂停所有用户线程,从应用程序的根对象(栈变量、静态字段、CPU寄存器等)出发,遍历所有可达对象,将存活对象的同步块索引标记位设为1,不可达对象不做标记。
计划阶段
GC扫描所有未标记的对象,将它们标记为可回收的垃圾对象,同时合并相邻的空闲内存块,生成空闲链表,为后续的内存整理做准备。
重定位阶段
GC移动所有存活对象到内存段的一端,消除内存碎片,同时更新所有指向这些对象的引用地址,确保所有引用都指向对象的新内存位置。
清扫阶段
GC将所有不可达对象占用的内存空间全部释放,更新托管堆的NextObjPtr指针,使其指向最后一个存活对象的末尾位置,恢复所有用户线程的执行。
资源释放
Dispose
通过实现IDisposable接口,手动释放非托管资源(文件句柄、数据库连接等),标准实现模式如下:
cs
public class FileResource : IDisposable
{
private IntPtr _fileHandle;
private bool _disposed = false;
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (_disposed) return;
if (disposing)
{
// 释放托管资源
}
// 释放非托管资源
Win32.CloseHandle(_fileHandle);
_disposed = true;
}
~FileResource() => Dispose(false);
}
DisposeAsync
.NET Core 2.1及以上版本支持IAsyncDisposable接口,用于异步释放资源,避免在释放IO密集型资源时阻塞线程:
cs
public class AsyncFileWriter : IAsyncDisposable
{
private StreamWriter _writer;
private bool _disposed = false;
public async ValueTask DisposeAsync()
{
if (_disposed) return;
if (_writer != null)
{
await _writer.DisposeAsync();
}
_disposed = true;
}
}
垃圾回收器的设置
工作站模式与服务器模式的垃圾回收
- 工作站GC:默认模式,适合桌面应用,使用单线程执行GC回收,占用资源少。
- 服务器GC:专为多CPU服务器场景优化,为每个CPU分配独立的GC堆和回收线程,并行执行回收操作,大幅提升高并发场景下的GC性能。
普通垃圾回收和后台垃圾回收
- 普通GC:回收过程中完全暂停所有用户线程,执行时间随Gen 2体积增大而变长。
- 后台GC:在.NET Core中默认启用,执行Gen 2回收时允许用户线程部分并行运行,大幅降低Full GC的暂停时间,仅在关键阶段短暂暂停线程。
设置延迟模式
- 通过GCSettings.LatencyMode可以调整GC的延迟策略:
- Batch:工作站GC的默认模式,最大化吞吐量,暂停时间较长。
- Interactive:平衡吞吐量和暂停时间,适合UI交互场景。
- LowLatency:低延迟模式,尽可能减少GC暂停时间,适合对响应速度要求极高的实时系统。
垃圾回收器参数调优
可以在runtimeconfig.json中配置GC参数优化性能:
cs
{
"runtimeOptions": {
"configProperties": {
"System.GC.Server": true,
"System.GC.Concurrent": true,
"System.GC.HeapHardLimitPercent": 70
}
}
}
同时推荐使用对象池(如ArrayPool<T>、RecyclableMemoryStream)复用大对象,减少大对象堆的分配压力,降低Full GC的触发频率。