【问题标题】:Why is Queue consuming so much memory?为什么队列消耗这么多内存?
【发布时间】:2019-07-18 10:26:28
【问题描述】:

基本上我在 codewars 网站上做一个code kata 在开始编码之前有点“热身”,并注意到一个问题,我不知道是因为我的代码,还是只是常规问题。

public static string WhoIsNext(string[] names, long n)
{
    Queue<string> fifo = new Queue<string>(names);

    for(int i = 0; i < n - 1; i++)
    {
        var name = fifo.Dequeue();
        fifo.Enqueue(name);
        fifo.Enqueue(name);
    }
    return fifo.Peek();
}

并且是这样调用的:

// Test 1
string[] names = { "Sheldon", "Leonard", "Penny", "Rajesh", "Howard" };
long n = 1; 
var nth = CodeKata.WhoIsNext(names, n); // n = 1 Should return sheldon.

// test 2
string[] names = { "Sheldon", "Leonard", "Penny", "Rajesh", "Howard" };
long n = 52; 
var nth = CodeKata.WhoIsNext(names, n); // n = 52 Should return Penny.

// test 3
string[] names = { "Sheldon", "Leonard", "Penny", "Rajesh", "Howard" };
long n = 7230702951; 
var nth = CodeKata.WhoIsNext(names, n); // n = 52 Should return Leonard.

在这段代码中,当我将 long n 的值放入 7230702951(一个非常高的数字......)时,它会引发内存不足异常。数字那么高,还是队列只是没有针对这些数字优化

我这样说是因为我尝试使用 List 并且列表内存使用量保持在 500 MB 以下(顺便说一句,平台大约是 327MB),并且运行了大约 2/3 分钟,而队列在几秒钟内引发了异常,仅在那段时间就超过了 2GB。

有人可以向我解释为什么会发生这种情况,我只是好奇吗?

编辑 1

我忘了添加列表代码:

public static string WhoIsNext(string[] names, long n)
{
    List<string> test = new List<string>(names);
    for(int i = 0; i < n - 1; i++)
    {
        var name = test[0];
        test.RemoveAt(0);

        test.Add(name);
        test.Add(name);
    }

    return test[0];
}

编辑 2

对于那些说代码加倍名称并且无效的人,我已经知道,代码不是有用的,只是一个 kata。 (我现在更新了链接!)

我的问题是,为什么 Queue 比具有高计数的 List 效率低得多。

【问题讨论】:

  • 嗯,这取决于names 中有多少值,可能很多很多,这显然会增加使用的内存。然后你将它们添加到队列中两次,不清楚这段代码实际上应该做什么。
  • 在我展示的示例中,字符串 [] 名称始终以 5 个名称开头。我只是好奇为什么队列会在几秒钟内超过 2gb,而列表即使在添加值时也一直保持在 326MB,我的意思是自从我开始写这个问题以来,我正在使用 7230702951 运行循环,以及内存使用情况不再增加。
  • 不确定,可以查看Queue<T> codeList<T> code
  • 代码其实很简单很短,其实就是那个函数,我会更新来展示我是如何调用这个函数的!

标签: c# memory queue


【解决方案1】:

部分原因是队列代码比List 代码快方式,因为队列是循环缓冲区,因此针对删除进行了优化。 列表不是 - 每次删除第一个元素时,列表复制数组内容

例如将输入值更改为72307000。在我的机器上,队列在不到一秒的时间内完成。几分钟后(按这个速度,几小时),这份名单仍在不断增加。 在 4 分钟内i 现在是 752408 - 它已经完成了几乎 1% 的工作。

因此,我不确定队列的内存效率较低。只是如此之快,您会更快地遇到内存问题。该列表几乎肯定存在相同的问题(ListQueue 将数组大小加倍的方式非常相似) - 可能需要几天时间才能解决。

在一定程度上,您可以预测这一点即使不运行您的代码。包含 7230702951 个条目的队列(运行 64 位)每个条目将占用最少 8 个字节。所以 57845623608 字节。大于 50GB。显然,您的机器很难将其放入 RAM 中(加上 .NET 不会让您拥有那么大的数组)...

此外,您的代码有一个细微的错误。 循环永远不会结束(如果n 大于int.MaxValue)。您的循环变量是int,但参数是long。您的int 将溢出(从int.MaxValueint.MinValuei++)。因此,对于较大的 n 值,循环将永远不会退出(意味着队列将永远增长)。您可能应该将i 的类型更改为long

【讨论】:

  • 是的!我现在注意到了这一点,并正在写一个这样的答案。我认为列表太慢了,垃圾收集器可以在列表不断添加内容的同时做他的事情,但是队列太快了!无论如何,谢谢你的回答!
  • 确切地说,我认为 GC 是@ErickSantander 的主要问题。阵列倍增系统(ListQueue 都使用)的性质意味着“过剩”内存使用量将(最多)略低于实际内存使用量。问题不在于List 的GC 跟不上。问题是List 太慢了,以至于当Queue 处理了 100% 的工作时,List 完成了不到 1%(嗯,更像是不到 0.01%)。毫不奇怪,它使用了不到 1% 的 RAM。 :)
猜你喜欢
  • 2019-09-30
  • 2018-10-10
  • 2015-01-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-11-09
  • 1970-01-01
相关资源
最近更新 更多