【问题标题】:Why is my code causing unmanaged memory to slowly increase causing gen0 collections?为什么我的代码导致非托管内存缓慢增加导致 gen0 集合?
【发布时间】:2023-03-25 04:04:01
【问题描述】:

当我在单元测试中运行以下代码时,我看到性能监视器中的私有字节和工作集缓慢增加,而 dotMemory 中的非托管内存缓慢增加。我已经在 dotPeek 中反编译了源代码,似乎找不到任何不寻常的地方。有任何想法吗?代码如下:

[TestMethod]
    public void TestEnumeratorMoveNext()
    {
        //Dictionary<string, int>[] outDict = new Dictionary<string, int>[32].Select(x => x = new int[100].ToDictionary(y => Guid.NewGuid().ToString())).ToArray();
        var outDict = new Dictionary<string, int>[32];
        for (int j = 0; j < 32; j++)
        {
            outDict[j] = new Dictionary<string, int>();
            for(int q = 0; q < 100; q++)
            {
                outDict[j].Add(Guid.NewGuid().ToString(), 0);
            }
        }

        for (int i = 0; i < 10000; i++)
        {
            var enumerator = new Enumerator<string, int>(outDict);

            while (enumerator.MoveNext()) { }

            Thread.Sleep(1000 / 60);
        }
    }

    public struct Enumerator<TKey, TValue> : IEnumerator<KeyValuePair<TKey, TValue>>
    {
        private Dictionary<TKey, TValue>[] dictionary;

        private int partition;
        private IEnumerator<KeyValuePair<TKey, TValue>> enumerator;
        private bool moveNext;

        private int totalPartitions;

        internal Enumerator(Dictionary<TKey, TValue>[] dictionary)
        {
            this.dictionary = dictionary;
            this.totalPartitions = dictionary.Count();

            partition = 0;
            enumerator = new Dictionary<TKey, TValue>.Enumerator();
            moveNext = false;
        }

        public bool MoveNext()
        {

            if (partition < totalPartitions)
            {
                do
                {
                    var outDict = dictionary[partition];

                    if (!moveNext)
                        enumerator = outDict.GetEnumerator();

                    moveNext = enumerator.MoveNext();

                    if (!moveNext)
                    {
                        enumerator.Dispose();
                        partition++;
                    }
                } while (!moveNext && partition < totalPartitions);

                return true;
            }

            partition = totalPartitions + 1;
            enumerator = new Dictionary<TKey, TValue>.Enumerator();
            moveNext = false;
            return false;
        }

        public KeyValuePair<TKey, TValue> Current
        {
            get
            {
                return enumerator.Current;
            }
        }

        public void Dispose()
        {
            enumerator.Dispose();
        }

        object IEnumerator.Current
        {
            get
            {
                return new KeyValuePair<TKey, TValue>(Current.Key, Current.Value);
            }
        }

        public void Reset()
        {
            throw new NotSupportedException();
        }
    }

感谢任何人能给我的任何帮助。提前致谢。

【问题讨论】:

  • 您的object IEnumerator.Current 每次被调用时都会创建一个 kvp,而公共实现只返回enumerator.Current;。我不确定这是否会导致问题,但 IEnumerator 实现看起来有点奇怪。
  • 我怀疑字节数会永远增加。为什么要睡觉?
  • 问题是慢慢地,GC 运行之前要到圣诞节。删除 Thread.Sleep() 并运行该方法一千次。现在您可以看到锯齿图案了。
  • @bokibeg 这真的不应该是问题,因为KeyValuePair&lt;K,V&gt; 是一个结构类型。简单地返回Current 会更明显,但也涉及到返回值的装箱。 Current.Key/Value 调用应该纯粹是堆栈,不会以任何方式影响堆。
  • 这就是 GC 的工作。避免在不知道垃圾收集器如何工作的情况下使用内存分析器,您无法理解所看到的内容。

标签: c# dictionary memory-leaks enumerator dotpeek


【解决方案1】:

解决方案是将 Enumerator 结构中的 private IEnumerator&lt;KeyValuePair&lt;TKey, TValue&gt;&gt; enumerator 更改为 private Dictionary&lt;TKey, TValue&gt;.Enumerator enumerator。不再有 G0 GC。如果有人知道为什么会这样,我很想听听解释。

【讨论】:

  • dotPeek 显示 outDict.GetEnumerator() 从 Dictionary.Enumerator 转换为 IEnumerator>。有什么拳击比赛吗? if (!this.moveNext) this.enumerator = (IEnumerator&lt;KeyValuePair&lt;TKey, TValue&gt;&gt;) dictionary.GetEnumerator();
  • Dictionary.Enumerator 是一个结构。正如我所说:如果您想避免 G0,请停止分配。也许您的问题应该是“我在这段代码中根本无法分配。分配在哪里?”。很多人都可以回答这个问题。
  • @usr 如果我错了,请纠正我,但创建结构不会导致 GC。结构是在堆栈上管理的,而不是在堆上。 GC 管理堆,而不是堆栈。
  • 那我一定是误会你了。我虽然你说使用结构消除了 GC 的发生。正如您现在所说,这是预期的。但是在这个答案中你说你不理解这个观察。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-06-20
  • 1970-01-01
相关资源
最近更新 更多