You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

.NET Core 2.0 GC未释放全部内存原因及优化,兼问控制台与Web应用GC差异

Hey there! Let's break down your questions step by step—first why your app still holds 140MB after triggering GC, then the key differences between GC behavior in console and web apps.

一、GC后内存仍占用140MB的原因及优化方法

可能的原因

Let's start with why you're seeing that persistent 140MB footprint even after forcing garbage collection:

  1. GC doesn't immediately return memory to the OS
    .NET's GC is designed to retain allocated memory as a "buffer" to avoid the overhead of repeatedly requesting memory from the operating system (especially in server-focused scenarios). The 140MB you see in Task Manager is the process's working set, which includes reserved but unused managed heap space—not actual live objects. The GC will reuse this reserved space for future allocations instead of giving it back to the OS right away.
  2. Large Object Heap (LOH) behavior
    Your List<Test>'s internal backing array is definitely a large object (since it holds 10 million instances, way over the 85KB LOH threshold). In .NET Core 2.0, LOH isn't compacted by default, and after collection, freed LOH memory isn't immediately returned to the OS. Even after nulling out the list, the LOH may still hold onto that reserved space.
  3. Runtime and non-managed overhead
    The .NET Core runtime itself (JIT compiler, thread stacks, system libraries, etc.) takes up a baseline amount of memory that the GC can't reclaim. This is normal and accounts for a portion of that 140MB.

优化建议

Here are actionable steps to reduce the memory footprint:

  • Check actual managed memory usage
    Don't rely solely on Task Manager—use tools like dotnet-counters (run dotnet counters monitor --process-id <pid> System.Runtime to view Heap Size, which reflects actual live managed memory) or Visual Studio's Memory Diagnostic tool. This will clarify if the 140MB is truly live objects or just reserved space.
  • Enable LOH compaction
    In .NET Core 2.0, you can force LOH compaction by setting the System.GC.HeapCompactionMode to CompactOnce. You can do this early in your code:
    System.GC.HeapCompactionMode = System.GCHeapCompactionMode.CompactOnce;
    
    Or add it to your runtimeconfig.json:
    {
      "runtimeOptions": {
        "configProperties": {
          "System.GC.HeapCompactionMode": "CompactOnce"
        }
      }
    }
    
    This will make the GC compact the LOH during your next collection, reducing fragmentation and making it more likely to return unused memory to the OS.
  • Switch to Server GC mode
    Console apps default to Workstation GC (optimized for low latency on single-core systems). Server GC (used by default in web apps) uses per-core heaps and has different memory retention policies. Enable it in runtimeconfig.json:
    {
      "runtimeOptions": {
        "configProperties": {
          "System.GC.Server": true
        }
      }
    }
    
    This may change how memory is retained after collection.
  • Use aggressive GC collection (last resort)
    Your current GC calls are close, but you can force full compaction with:
    GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced, true, true);
    GC.WaitForPendingFinalizers();
    GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced, true, true);
    
    The final two true parameters enable heap compaction and wait for all finalizers to run. Note that manual GC is generally not recommended in production, but it’s useful for testing.

二、控制台应用与Web应用的GC机制差异

The core differences come down to default GC modes and optimization priorities:

  • Default GC Mode
    • Console apps: Default to Workstation GC, optimized for low latency (ideal for interactive apps). It uses a single heap and prioritizes minimizing pause times over throughput.
    • Web apps (ASP.NET Core): Default to Server GC, optimized for high throughput on multi-core servers. It creates a heap per CPU core, enabling parallel collections and better utilization of multi-core hardware. Server GC retains more memory as a buffer to avoid frequent OS memory requests.
  • Memory Retention Policies
    Server GC holds onto more reserved memory because web apps have steady, ongoing allocations—reusing memory is faster than requesting more from the OS. Workstation GC is more willing to return memory to the OS when unused, though it still retains a small buffer.
  • Collection Triggering
    Web apps have continuous incoming requests, so GC triggers more frequently based on memory pressure. Console apps (like your test) often have bursty allocations followed by idle time, so GC may only trigger when memory is low or when manually called.
  • Background GC Behavior
    Both support background GC, but Server GC runs background collections in parallel across cores, while Workstation GC uses a single background thread to minimize impact on foreground work.

内容的提问来源于stack exchange,提问作者user9159375

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.15 08:36:57