实例化许多短期字典<string, MyClass>可以吗?还是对象池更好?

问题描述 投票:0回答:1

我有一个方法可以从数据库中读取一些值,然后生成一个字典 - 可能会读取数千次。

public IEnumerable<Dictionary<string, MyClass>> GetValues()
{
   var results = new Dictionary<string, MyClass>();
   // ... fill data from data reader

   yield return results;
}

此方法的使用者仅迭代结果并以阅读方式使用字典。 所以......关于垃圾收集和内存连续性:这样做有意义吗?

private Dictionary<string, MyClass> _resultHolder = new Dictionary<string, MyClass>();

public IEnumerable<ReadOnlyDictionary<string, MyClass>> GetValues()
{
   // Don't want the previous record values, so clear
   _resultHolder.Clear();
   // ... fill data from data reader

   yield return _resultHolder;
}

据我所知,垃圾收集器有不同的级别...那么这个结果是否只会以级别 0 结束,因此不会导致任何内存碎片问题? 这种对象池会不会是过早的优化?

garbage-collection
1个回答
0
投票

我知道这是一个旧答案,但在搜索时遇到了它 类似的优化。 正如您所说,.NET 中的垃圾收集在不同的级别上运行 - 这些级别称为 Generations

来源:

  1. .NET垃圾收集
  2. .NET 垃圾收集基础知识
  3. .NET 垃圾收集大对象堆
  4. .NET 垃圾收集性能

垃圾收集世代

  • Generation 0:垃圾收集在此发生最频繁 这一代包含短暂的对象(例如, 临时变量)。在这一代发生垃圾收集后,GC 会压缩剩余对象的内存,并将它们提升到第 1 代

  • 第 1 代:在第 0 代中尚未收集的对象 晋升为第 1 代。这一代还包含 短命对象,并作为短命对象和短命对象之间的缓冲区 长寿的物体。

  • 第 2 代:这一代由长寿命对象组成, 已在第 1 代和第 2 代的垃圾收集中幸存下来。在这一代中触发的垃圾收集将收集前几代以及LOH中的对象。

  • LOH 或第 3 代:.NET 的垃圾收集器将大型对象放置在 大对象堆 (LOH) - 大对象被视为 >= 85000 字节。这是因为大对象具有多种性能影响,例如压缩(本质上是将对象从一个内存地址复制到另一个内存地址)。

一代上发生的每个垃圾收集周期也会收集较小一代的对象。这意味着老一代上发生的垃圾收集比年轻一代上发生的垃圾收集要昂贵得多 - 特别是在压缩内存(即碎片)方面。在第 2 代垃圾收集之后压缩内存比在第 0 代垃圾收集之后压缩内存要昂贵得多(因为LOH 以及所有代中发生的完整 GC)。 选项 1 与选项 2

如果

Dictionary 的尺寸始终很小 (


如果 <85000 bytes)Dictionary
的大小很小,选项 1 的优点是
Dictionary
是局部变量,因此,它将最终出现在
Generation 0
上,并被垃圾收集器非常频繁地收集。如上所述,内存压缩(内存压缩称为碎片问题)在这一层上的成本很低,因此,性能将得到优化。

如果

Dictionary的大小很大(>=85000字节)


如果 Dictionary
的尺寸很大,这意味着该物体最终将始终处于
LOH
中。使用选项 1,实例化了许多新的 Dictionary,这意味着
LOH
最终将拥有很多对象。这将对选项 1 造成性能损失,特别是当发生第 2 代垃圾收集时 - 主要是由于大型对象的内存压缩。在这种情况下,选项 2 将是最佳选择,因为只维护一个对象。 结论

最终,这一切都会深入到特定的用例和您正在使用的数据类型。我将通过利用生产收集的数据来检查构建

Dictionary

的数据,然后进一步决定要采取的行动方案。

    

© www.soinside.com 2019 - 2024. All rights reserved.