为什么.Net中进程内存会增长,但托管堆大小却没有改变?

问题描述 投票:0回答:1

我在 C# 中遇到了奇怪的内存泄漏,这对我来说没有任何意义。这是重现此问题的简单示例。 我有一个简单的类,可以保存字节数组:

public class Record
{
    public byte[] Bytes { get; set; }
}

我还创建了一个方法,可以显示进程占用的总内存(以兆字节为单位),以及托管堆内存的大小:

static void ShowMemory()
{
    var managedTotal = Math.Round(GC.GetTotalMemory(true) / (1024.0 * 1024.0), 2);
    var proc = System.Diagnostics.Process.GetCurrentProcess();
    var memoryTotal = Math.Round(proc.PrivateMemorySize64 / (1024.0 * 1024.0), 2);
    proc.Dispose();
    Console.WriteLine($"Total Memory: {memoryTotal} Mb, Managed Memory: {managedTotal} Mb");
}

现在这是有趣的部分,这里是演示问题的代码片段:

var list = new List<Record>();
// here I just add some records to the list and allocate Bytes array for each of the records.
for (int i = 0; i < 10000; i++)
{
    list.Add(new Record() { Bytes = new byte[100000]  });
}

// Now here I display the memory. The result I get is:
// Total Memory: 1071.84 Mb, Managed Memory: 954.56 Mb
ShowMemory();

// here I loop through the list again and reallocate the Bytes array
foreach (var record in list)
{
    record.Bytes = new byte[100000];
}

// Now here I display the memory again. The result I get is:
// Total Memory: 1994.46 Mb, Managed Memory: 954.68 Mb
ShowMemory();

现在您可以看到显示的结果是:

Total Memory: 1071.84 Mb, Managed Memory: 954.56 Mb
Total Memory: 1994.46 Mb, Managed Memory: 954.68 Mb

我不明白在我再次循环列表并重新分配字节后,总内存的大小增长了一倍,而堆内存几乎保持不变,这怎么可能。此外,如果我将此代码放在单独的方法中,完成后,垃圾收集器仅重新调整托管内存,而总内存仍然很大。

有人可以解释一下这里发生了什么吗?也许如何避免它?由于这个问题,我运行的另一个进程耗尽了内存。

仅供参考,我正在使用

.Net 8
运行于
Ubuntu 22

c# .net memory-leaks garbage-collection heap-memory
1个回答
0
投票

这不是您问题的答案,而是更多建议您应该使用 .NET 8.0 中提供的池内存实用程序。例如:

for (int i = 0; i < 10000; i++)
{
    list.Add(new Record() { Bytes = ArrayPool<byte>.Shared.Rent(100000)  });
}

ShowMemory();

foreach (var record in list)
{
    ArrayPool<byte>.Shared.Return(record.Bytes);
    record.Bytes = ArrayPool<byte>.Shared.Rent(100000);
}

在我的机器上打印:

Total Memory: 1274.11 Mb, Managed Memory: 1250.87 Mb
Total Memory: 1274.38 Mb, Managed Memory: 1250.87 Mb

当然,您需要做更多的工作,因为

Rent()
可能会返回一个更大的块。我建议研究一下 Memory、Span、MemoryOwner 等。

© www.soinside.com 2019 - 2024. All rights reserved.