大数组如何分配内存



我正在寻找一种在不浪费大量内存的情况下将大型三维稀疏阵列结构保存到内存中的方法。在这里,我用长数组做了一个实验:

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环境变量(项目属性->调试)设置为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。"为什么不调整它的大小?"。Resize生成一个新数组并复制值。这是复制、记忆的时候了,最后邪恶的GC来吃掉你。

我浪费了记忆吗。(我不记得了。外星人?!)如果是,多少钱?0、(3840-168)MB还是(15369-8-168)MB?

结语:

评论是评论还是回答?

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

答案会给出答案吗?神秘的(更多音乐)

(斯库利:穆德,蟾蜍刚从天上掉下来!穆德:我猜他们的降落伞没有打开。)

谢谢大家!

工作集不是分配的内存量。它是当前可用于流程的一组页面。Windows围绕这一点实施了各种策略,而这个数字通常很难解释。

在这里,内存很可能是从操作系统请求为零的。对页面的第一次访问实际上会使零页面可用。

您应该查看私有字节。

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

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

是的,从CLR和运行的.NET代码的角度来看。操作系统可能会玩一些把戏,比如在第一次读取或写入时在页面中懒散地出错。

对于《第二集》,答案是错误既发生在阅读上,也发生在写作上。我不太了解第三集的内容,但我认为它只涉及较少的页面。

我是否浪费了内存

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

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

请考虑使用内存映射文件。在这里,文件是后备存储,RAM被视为缓存。这将以完全相同的方式表现。

最新更新