LOH(Large Object Heap)是.NET中存放≥85,000字节对象的内存区域,因不参与GC压缩而易碎片化;高并发下频繁分配大对象会触发full GC,导致线程暂停、吞吐下降。
LOH(Large Object Heap)是 .NET 中专门存放 ≥ 85,000 字节对象的内存区域。它不参与常规 GC 的 compact 阶段,意味着一旦分配,即使后续被回收,留下的碎片也不会被自动整理。在高并发场景下,频繁创建大对象(如 byte[]、string、大型 DTO 序列化结果)会导致 LOH 快速膨胀、碎片加剧,最终触发 full GC(即 blocking GC),暂停所有线程 —— 这就是吞吐骤降、延迟毛刺的常见根源。
很多看似“轻量”的操作,在高并发放大后会悄悄落入 LOH。关键不是“你写了 new byte[100000]”,而是框架/库在背后替你干了这事:
HttpClient.GetStringAsync() 返回的 string 如果响应体超 85KB,字符串本身进 LOH(UTF-16 编码,实际阈值 ≈ 42,500 字符)JsonSerializer.Serialize(obj) 输出的 
string 或 byte[] 容易越界,尤其嵌套深、字段多的对象MemoryStream.ToArray() —— 每次调用都复制整个缓冲区到新 byte[],极易命中 LOHActionResult 自动序列化返回值,T 若含大集合或长文本,风险极高别猜,用工具看真实行为。最直接的方式是启用 .NET 运行时事件追踪:
dotnet-trace collect --process-id--providers Microsoft-DotNETCore-EventPipe::0x1000000000000000:4
然后用 dotnet-counters monitor -p 观察关键指标:
LOH Size:持续增长且不回落 → 内存泄漏或碎片堆积% Time in GC > 5% 且伴随 Gen 2 GC Count 频繁上升 → LOH 触发 full GCAlloc Rate / sec 突增时,若 LOH Alloc Rate / sec 同步飙升 → 确认是大对象主导分配注意:.NET 6+ 默认启用 GCHeapHardLimit 和 LOH 压缩(需显式开启),但压缩本身有开销,不能无脑打开。
高并发服务的关键不是“不分配大对象”,而是“不让大对象生命周期和请求强绑定”。实操上优先考虑流式处理与池化:
Stream 替代 byte[]:比如 JsonSerializer.SerializeAsync(stream, obj) 直接写入响应流,不缓存完整 payloadArrayPool.Shared.Rent() 分配缓冲区,严格 .Return(),避免每次 newReadOnlyMemory + Span 解析,跳过 string 实例化ResponseCompression 可显著减小传输体积,间接降低序列化后对象大小LOH 问题从来不是孤立的内存问题,它是并发模型、序列化方式、IO 路径共同作用的结果。最危险的不是某次分配,而是把 LOH 分配藏在中间件、过滤器或通用泛型方法里 —— 看似安全,压测一跑就崩。