大数组如何分配内存?

时间:2016-06-02 08:48:19

标签: c# arrays .net-core-rc2

我正在寻找一种方法来将大的3D稀疏数组结构保存到内存中而不会浪费大量内存。在这里,我做了一个longs数组的实验:

using System;
using System.Diagnostics;
using System.Runtime;

namespace ConsoleApp4
{
    public class Program
    {
        static Process proc = Process.GetCurrentProcess();
        const int MB = 1024 * 1024;
        const int IMAX = 5;
        const int JMAX = 100000000;
        public static void ShowTextWithMemAlloc(string text)
        {
            proc.Refresh();
            Console.WriteLine($"{text,-30}WS64:{proc.WorkingSet64/MB,5}MB  PMS64:{proc.PrivateMemorySize64/MB,5}MB");
            Console.ReadKey();
        }
        public static void Main(string[] args)
        {
            Console.Write(" ");
            ShowTextWithMemAlloc("Start.");
            long[] lArray = new long[IMAX * JMAX];
            long[] l1Array = new long[IMAX * JMAX];
            long[] l2Array = new long[IMAX * JMAX];
            long[] l3Array = new long[IMAX * JMAX];
            ShowTextWithMemAlloc("Arrays created.");
            lArray[IMAX * JMAX - 1] = 5000;
            l1Array[IMAX * JMAX - 1] = 5000;
            l2Array[IMAX * JMAX - 1] = 5000;
            l3Array[IMAX * JMAX - 1] = 5000;
            ShowTextWithMemAlloc("Last elements accessed.");
            for (var i=IMAX-1; i>= 0; i--)
            {
                for (var j=0; j<JMAX; j++)
                {
                    lArray[i * JMAX + j] = i * JMAX + j;
                }
                ShowTextWithMemAlloc($"Value for row {i} assigned.");
            }
            //lArray = new long[5];
            //l1Array = null;
            //l2Array = null;
            //l3Array = null;
            //GCSettings.LargeObjectHeapCompactionMode = GCLargeObjectHeapCompactionMode.CompactOnce;
            //GC.Collect();
            //ShowTextWithMemAlloc($"GC.Collect done.");
            ShowTextWithMemAlloc("Stop.");
        }
    }
}

如果要测试它,请将COMPlus_gcAllowVeryLargeObjects环境变量(Project Properties - &gt; Debug)设置为1或更改JMAX。这是输出:

 Start.                        WS64:   14MB  PMS64:    8MB
 Arrays created.               WS64:   15MB  PMS64:15360MB
 Last elements accessed.       WS64:   15MB  PMS64:15360MB
 Value for row 4 assigned.     WS64:  779MB  PMS64:15360MB
 Value for row 3 assigned.     WS64: 1542MB  PMS64:15360MB
 Value for row 2 assigned.     WS64: 2305MB  PMS64:15361MB
 Value for row 1 assigned.     WS64: 3069MB  PMS64:15361MB
 Value for row 0 assigned.     WS64: 3832MB  PMS64:15362MB
 Stop.                         WS64: 3844MB  PMS64:15325MB

当我看到任务管理器中的内存消耗在Process.WorkingSet64中是这样的。实数是多少?为什么分配内存?数组实际上是一个连续分配的内存吗?数组是一个数组吗?外星人存在吗? (戏剧性的背景音乐)

第2集: 我们做了一个小改动:

            //lArray[i * JMAX + j] = i * JMAX + j;
            var x= lArray[i * JMAX + j];

并且没有任何变化(在输出中)。存在与不存在的区别在哪里? (更具戏剧性的背景音乐)现在我们正在等待一个神秘人物的回答(他们的名字下面有一些数字和一个小'k'。

第3集: 另一个变化:

    //lArray[IMAX * JMAX - 1] = 5000;
    //l1Array[IMAX * JMAX - 1] = 5000;
    //l2Array[IMAX * JMAX - 1] = 5000;
    //l3Array[IMAX * JMAX - 1] = 5000;
    //ShowTextWithMemAlloc("Last elements accessed.");
    long newIMAX = IMAX-3;
    long newJMAX = JMAX / 10;
    for (var i=0; i<newIMAX; i++)
    {
        for (var j=0; j<newJMAX; j++)
        {
            lArray[i * newJMAX + j] = i * newJMAX + j;
            //var x= lArray[i * JMAX + j];
        }
        //ShowTextWithMemAlloc($"Value for row {i} assigned.");
    }
    ShowTextWithMemAlloc($"{newIMAX*newJMAX} values assigned.");

输出:

 Start.                             WS64:   14MB  PMS64:    8MB
 Arrays created.                    WS64:   15MB  PMS64:15369MB
 20000000 values assigned.          WS64:  168MB  PMS64:15369MB
 Stop.                              WS64:  168MB  PMS64:15369MB

PMS64用于一个阵列(15369-8)/ 4 = 3840MB 这不是稀疏数组,而是部分填充的数组;)。我正在使用这个168MB。

回答一些问题“你为什么不使用确切的尺寸?”。因为我不知道吗?数据可以来自多个用户定义的SQL。 “你为什么不调整它?”调整大小创建一个新数组并复制值。这是时候复制,记忆,最后邪恶的GC来吃你。

我是否浪费了记忆力。 (我不记得了。外星人?!) 什么时候,是多少? 0,(3840-168)MB或(15369-8-168)MB?

结语:

评论是评论还是答案?

  

连续内存实际是连续的内存吗?

答案会给出答案吗?神秘。 (more music

Scully :Mulder,蟾蜍从天而降! Mulder :我猜他们的降落伞没有打开。)

谢谢大家!

1 个答案:

答案 0 :(得分:6)

工作集不是分配的内存量。它是当前可用于该过程的页面集。 Windows实现了各种各样的策略,这个数字通常很难解释。

这里,可能从OS请求存储器归零。第一次访问页面实际上可以使归零页面可用。

您应该查看私有字节。

您不能稀疏地分配.NET数组。或许,您应该考虑使用一些提供稀疏数组印象的数据结构。

  

数组实际上是连续分配的内存吗?

是的,从CLR和.NET代码运行的角度来看。操作系统可能会玩弄技巧,例如在第一次读取或写入时页面中出现延迟故障。

对于“第2集”,答案是错误发生在读取和写入上。我并不完全遵循第3集的内容,但我认为它只涉及更少的页面。

  

我是否浪费了记忆

这更复杂。只要页面没有被触摸,它们就不会被物理使用。例如,它们可用于文件缓存或用于驻留工作集的其他程序。但是,他们确实会计入系统的承诺费用。 Windows保证您可以将这些页面提供给您。在某些随机内存访问中,您不会耗尽内存。 Linux不保证这一点。它有OOM杀手作为缓解。

在极端情况下,如果你像这样分配1TB,你需要RAM和页面文件大小的总和超过1TB,即使这些空间都不会被最终使用。

考虑使用内存映射文件。这里,文件是后备存储,RAM被视为缓存。这样做的方式完全相同。

相关问题