【问题标题】:Why does a .net program use more VirtualMemorySize64 when the computer has more ram当计算机有更多内存时,为什么 .net 程序使用更多 VirtualMemorySize64
【发布时间】:2019-01-30 13:43:39
【问题描述】:

我创建了一个简单的测试应用程序,它使用二进制数组分配 100mb。

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Text;

namespace VirtualMemoryUsage
{
class Program
{
    static void Main(string[] args)
    {
        StringBuilder sb = new StringBuilder();
        sb.AppendLine($"IsServerGC = {System.Runtime.GCSettings.IsServerGC.ToString()}");

        const int tenMegabyte = 1024 * 1024 * 10;
        long allocatedMemory = 0;
        List<byte[]> memory = new List<byte[]>();
        for (int i = 0; i < 10; i++)
        {
            //alloc 10 mb memory
            memory.Add(new byte[tenMegabyte]);
            allocatedMemory += tenMegabyte;
        }

        sb.AppendLine($"Allocated memory:    {PrettifyByte(allocatedMemory)}");
        sb.AppendLine($"VirtualMemorySize64: {PrettifyByte(Process.GetCurrentProcess().VirtualMemorySize64)}");
        sb.AppendLine($"PrivateMemorySize64: {PrettifyByte(Process.GetCurrentProcess().PrivateMemorySize64)}");
        sb.AppendLine();
        Console.WriteLine(sb.ToString());
        Console.ReadLine();
    }

    private static object PrettifyByte(long allocatedMemory)
    {
        string[] sizes = { "B", "KB", "MB", "GB", "TB" };
        int order = 0;
        while (allocatedMemory >= 1024 && order < sizes.Length - 1)
        {
            order++;
            allocatedMemory = allocatedMemory / 1024;
        }
        return $"{allocatedMemory:0.##} {sizes[order]}";
    }
 }
}

注意:对于这个测试,在 app.config 中将 gcserver 设置为 true 很重要

<runtime>
  <gcServer enabled="true"/>  
</runtime>

这将显示进程分配的 PrivateMemorySize64 和 VirtualMemorySize64 的数量。

虽然 PrivateMemorySize64 在不同的计算机上保持相似,但 VirtualMemorySize64 变化很大。

在分配相同数量的内存时,VirtualMemorySize64 出现这种差异的原因是什么?有这方面的文档吗?

【问题讨论】:

  • 当系统内存不足时,垃圾收集器触发并释放繁忙的。只要有足够的内存,垃圾收集器就会休眠。
  • 潜在原因:因为它可用。官方文档:不太可能,因为 GC 内部可能会发生变化。您是否在打开其他几个程序时交叉检查数字是否发生变化,从而导致可用 RAM 量减少?另外:该内存是保留还是已提交?保留内存非常快。提交内存很慢。
  • @AlexanderPetrov:小心,GC 可能会释放内存,但可能不会返回操作系统,只是内部的 .NET。如果对 Manuel 的观察导致了单个 VirtualAlloc 内存块,它永远无法将其归还。

标签: c# .net memory


【解决方案1】:

哇,你真幸运。在我的机器上,最后一行显示 17 GB!

Allocated memory:    100M
VirtualMemorySize64: 17679M
PrivateMemorySize64: 302M

虽然 PrivateMemorySize64 在不同的计算机上保持相似 [...]

私有字节是仅属于您的程序的字节。它几乎不受其他事物的影响。它包含您堆上的内容,其他人无法访问。

为什么是 302 MB 而不仅仅是 100 MB? SysInternals VMMap 是分解该值的好工具:

私有字节的颜色和大小说:

  • 紫色 (7.5 MB):图像文件,即不可共享的 DLL
  • 橙色 (11.2 MB):堆(非 .NET)
  • 绿色 (103 MB):托管堆
  • 橙色 (464 kB):堆栈
  • 黄色 (161 MB):私人数据,例如TEB和PEB
  • 棕色 (36 MB):页表

如您所见,.NET 在托管堆中只有 3 MB 开销。剩下的就是任何流程都需要做的其他事情。

调试器或分析器可以帮助分解托管堆:

0:013> .loadby sos clr
0:013> !dumpheap -stat
[...]
000007fedac16878      258        11370 System.String
000007fed9bafb38      243        11664 System.Diagnostics.ThreadInfo
000007fedac16ef0       34        38928 System.Object[]
000007fed9bac9c0      510       138720 System.Diagnostics.NtProcessInfoHelper+SystemProcessInformation
000007fedabcfa28        1       191712 System.Int64[]
0000000000a46290      305       736732      Free
000007fedac1bb20       13    104858425 System.Byte[]
Total 1679 objects

所以你可以看到.NET“默认”需要一些字符串和其他对象。

分配相同数量的内存时,VirtualMemorySize64 出现这种差异的原因是什么?

0:013> !address -summary
[...]
--- State Summary ---------------- RgnCount ----------- Total Size -------- %ofBusy %ofTotal
MEM_FREE                                 58      7fb`adfae000 (   7.983 TB)           99.79%
MEM_RESERVE                              61        4`3d0fc000 (  16.954 GB)  98.11%    0.21%
MEM_COMMIT                              322        0`14f46000 ( 335.273 MB)   1.89%    0.00%

仅提交了 335 MB。那是实际可以使用的内存。 16.954 GB 只是保留。目前无法使用它们。它们既不在 RAM 中,也不在页面文件的磁盘中。分配保留内存非常快。我经常看到 17 GB 的值,尤其是在 ASP.NET 故障转储中。

再看VMMap中的细节

我们可以看到 17 GB 只是分配在一个块中。对您的问题的评论说:“当系统内存不足时,垃圾收集器会触发并释放繁忙的垃圾收集器。”但是,要通过 VirtualFree() 释放 VirtualAlloc() 的块,该块在逻辑上不能为空,即内部不应有单个 .NET 对象 - 这不太可能。所以它会永远留在那里。

有哪些可能的好处?它是一个连续的内存块。如果你现在需要new byte[4G](),它就可以工作。

最后,可能的原因是:它已经完成了,因为它不会受到伤害,无论是 RAM 还是磁盘。并且在需要时,可以在以后的某个时间点提交。

有这方面的文档吗?

这不太可能。详细的 GC 实现可能会随着 .NET 的下一个版本而改变。我认为微软没有记录这一点,否则如果行为改变,人们会抱怨。

有些人写过像 this one 这样的博客文章,告诉我们某些值可能取决于处理器的数量。

0:013> !eeheap -gc
Number of GC Heaps: 4
[...]

我们在这里看到的是,.NET 创建的堆与处理器一样多。这对于垃圾收集很有用,因为每个处理器都可以独立收集一个堆。

【讨论】:

    【解决方案2】:

    您使用的指标不是分配的内存,而是内存used by the process。一个 - private,另一个 - shared 与您机器上的其他进程。实际内存量 used by the process 取决于可用内存量和正在运行的其他进程。

    编辑:Thomas Weller 的回答提供了比我的 Microsoft 链接更多的关于该主题的详细信息

    它不一定代表您的应用程序执行的分配量。如果您想估计分配的​​内存(不包括 .NET 框架库和内存分页开销等),您可以使用

    long memory = GC.GetTotalMemory(true);
    

    true 参数告诉 GC 首先执行垃圾收集(它不是必须的)。未使用但未收集的内存计入您询问的值中。如果系统有足够的内存,它可能在需要时才被收集。在这里您可以找到additional information 了解 GC 的工作原理。

    【讨论】:

    • 嗯,“使用”这个词是相对的。如果您在 32 位进程中使用 VirtualAlloc() 4 GB,那么其他任何人都无法获得内存。它是保留、提交还是在工作集中都没有关系。它只是“消失”了。 OP 从来没有谈论过工作集的大小,那你为什么要在这里提出呢?他不太可能对此感到困惑。
    • @ThomasWeller,我提出来了,因为基于这个问题,我有强烈的感觉,这是他试图通过执行测试来找出的。主要基于:What is the reason for this differences in VirtualMemorySize64 when the same amount of memory is allocated?我可能完全错了,你很好地解释了相对“使用”的去向。
    猜你喜欢
    • 2021-07-13
    • 1970-01-01
    • 2018-09-29
    • 2012-11-10
    • 2023-03-30
    • 1970-01-01
    • 1970-01-01
    • 2020-10-21
    • 1970-01-01
    相关资源
    最近更新 更多