.NET Core 内存管理

内存分配

栈空间和堆空间

栈空间用于存储值类型变量、方法参数等,由系统自动分配释放,无需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的触发频率。

相关推荐
无风听海7 小时前
.NET 10之Span技术总结
.net
1-1=08 小时前
.NET Core 配置与选项
.net·.netcore·1024程序员节
mudtools8 小时前
在飞书 SDK 上用 Microsoft Agent Framework 搭一个能干活的工作搭子
人工智能·ai·.net·飞书·agent
ChaITSimpleLove8 小时前
Layout.WatchDog.NET 1.5.0-beta.3 发布:统一全后端雪花 ID 策略,数据库能力矩阵一览
.net·watchdog·日志记录器·节点资源监控看板·.net专属轻量级日志中心
IT规划师9 小时前
MQTT 实战 - 放下炫技,8 年踩坑的真心话与方法论(收官)
物联网·.net
CSharp精选营9 小时前
.NET 接口从 800ms 干到 60ms:生产环境这几个优化点最关键
性能优化·c#·.net·efcore
CSharp精选营1 天前
制造业 ERP 物料建模:主料、辅料和 BOM 到底怎么拆才稳
.net·erp·bom·制造业·物料建模
玩泥巴的2 天前
在飞书 SDK 上用 Microsoft Agent Framework 搭一个能干活的工作搭子
ai·.net·飞书·agent