我正在寻找一种方法来将大的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 :我猜他们的降落伞没有打开。)
谢谢大家!
答案 0 :(得分:6)
工作集不是分配的内存量。它是当前可用于该过程的页面集。 Windows实现了各种各样的策略,这个数字通常很难解释。
这里,可能从OS请求存储器归零。第一次访问页面实际上可以使归零页面可用。
您应该查看私有字节。
您不能稀疏地分配.NET数组。或许,您应该考虑使用一些提供稀疏数组印象的数据结构。
数组实际上是连续分配的内存吗?
是的,从CLR和.NET代码运行的角度来看。操作系统可能会玩弄技巧,例如在第一次读取或写入时页面中出现延迟故障。
对于“第2集”,答案是错误发生在读取和写入上。我并不完全遵循第3集的内容,但我认为它只涉及更少的页面。
我是否浪费了记忆
这更复杂。只要页面没有被触摸,它们就不会被物理使用。例如,它们可用于文件缓存或用于驻留工作集的其他程序。但是,他们确实会计入系统的承诺费用。 Windows保证您可以将这些页面提供给您。在某些随机内存访问中,您不会耗尽内存。 Linux不保证这一点。它有OOM杀手作为缓解。
在极端情况下,如果你像这样分配1TB,你需要RAM和页面文件大小的总和超过1TB,即使这些空间都不会被最终使用。
考虑使用内存映射文件。这里,文件是后备存储,RAM被视为缓存。这样做的方式完全相同。