当计算机具有更多RAM时,为什么.net程序使用更多VirtualMemorySize64

时间:2018-08-24 05:59:41

标签: c# .net memory

我创建了一个简单的测试应用程序,该应用程序使用二进制数组分配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却相差很大。 enter image description here

当分配相同数量的内存时,VirtualMemorySize64中出现此差异的原因是什么?是否有任何相关文档?

2 个答案:

答案 0 :(得分:2)

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

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

虽然PrivateMemorySize64在不同计算机上仍然相似[...]

私人字节是仅属于您的程序的字节。它几乎不受其他因素的影响。它包含您堆中的东西,其他人无法访问。

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

VMMap about private bytes

专用字节的颜色和大小如下:

  • 紫色(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中的详细信息

VMMap managed heap details

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

可能有什么好处?它是一个连续的内存块。如果您现在需要new byte[4G](),它将可以正常工作。

最后,可能的原因是:这样做是因为它不会损害RAM和磁盘。并且在需要时可以在以后的某个时间提交。

  

是否有关于此的文档?

这不太可能。下一代.NET可能会更改详细的GC实现。我认为Microsoft不会对此进行记录,否则如果行为发生变化,人们会抱怨。

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

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

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

答案 1 :(得分:1)

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

编辑:Thomas Weller的答案提供了比我的Microsoft链接更多的关于该主题的细节

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

long memory = GC.GetTotalMemory(true);

其中true参数告诉GC首先执行垃圾收集(不必这样做)。您询问的值中包括未使用但未收集的内存。如果系统有足够的内存,则可能直到需要时才将其收集。在这里您可以找到additional information关于GC如何工作的信息。