【问题标题】:Can you use List<List<struct>> to get around the 2gb object limit?您可以使用 List<List<struct>> 绕过 2gb 对象限制吗?
【发布时间】:2012-04-11 04:16:30
【问题描述】:

我遇到了 c# 中的 2gb 对象限制(由于某些烦人的原因,这甚至适用于 64 位),并且有大量结构(预计总大小为 4.2 gig)。

现在显然使用 List 会给我一个大小为 4.2gb 的列表,但会使用由较小列表组成的列表,这些列表又包含一部分结构,允许我跳过这个限制吗?

我的理由是,这只是 CLR 中的硬编码限制,它阻止我在 64 位平台上实例化 9gig 对象,它与系统资源完全无关。列表和数组也是引用类型,因此包含列表的列表实际上只包含对每个列表的引用。因此没有一个对象超过大小限制。

这有什么不可行的原因吗?我现在会亲自尝试,但我手头没有内存分析器来验证。

【问题讨论】:

  • 一个有趣的 C# 问题已经 4 分钟没有答案。我感觉到 Jon Skeet 正在接近……
  • 出于好奇,它必须是List&lt;T&gt; 还是List&lt;List&lt;T&gt;&gt;?你能用LinkedList&lt;T&gt;
  • @JamesMichaelHare 你可以,但是你会有大量的额外开销,因为每个LinkedListNode&lt;T&gt; 都是一个独立的类,有多个引用。
  • @digEmAll 如果你的结构足够大,那么它根本不应该是一个结构......设计指南说任何> 16字节的对象都应该是一个类:@987654322 @LinkedListNode&lt;T&gt; 单独有 3 个引用,在 x64 上,单独是 24 个字节......你也有完全不同的性能语义与 LinkedList 与 List。

标签: c# memory memory-management reference 64-bit


【解决方案1】:

现在显然使用 List 会给我一个大小为 4.2gb 的列表,但会使用由较小列表组成的列表,这些列表又包含一部分结构,允许我跳过这个限制吗?

是的 - 不过,如果您想解决这个限制,我会考虑自己使用数组,而不是让 List&lt;T&gt; 类管理数组。

CLR 中的 2gb 单个对象限制正是如此,单个对象实例。当您创建一个结构数组(List&lt;T&gt; 在内部使用)时,整个数组是 CLR 中的“一个对象实例”。但是,通过使用List&lt;List&lt;T&gt;&gt; 或交错数组,每个内部列表/数组都是一个单独的对象,这使您可以有效地拥有您想要的任何大小的对象。

CLR 团队实际上在博客上对此进行了介绍,并提供了一个 sample BigArray&lt;T&gt; 实现,其作用类似于单个 List&lt;T&gt;,但在内部为您进行“块”管理。这是获取 >2gb 列表的另一种选择。

请注意,.NET 4.5 可以选择提供larger than 2gb objects on x64,但您必须明确选择加入。

【讨论】:

    【解决方案2】:

    List 包含 4 或 8 字节的引用,具体取决于您是在 32 位还是 64 位模式下运行,因此如果您引用 2GB 的对象不会增加实际的 List 大小到 2 GB,但它只会增加引用该对象所需的字节数。

    这将允许您引用数百万个对象,每个对象可以是 2GB。如果List 中有 4 个对象,每个对象为 2 GB,那么 List 引用的对象价值为 8 GB,但 List 对象只会用完额外的 4*8=32字节。

    List 达到 2GB 限制之前,您可以在 32 位计算机上保存的引用数量为 5.3687 亿,在 64 位计算机上为 2.6843 亿。

    5.36 亿次引用 * 2 GB = 大量数据!

    附: Reed 指出,上述情况适用于引用类型,但不适用于值类型。因此,如果您持有值类型,那么您的解决方法是有效的。请参阅下面的评论以获取更多信息。

    【讨论】:

    • List&lt;T&gt;,当与值类型一起使用时,不保存引用(对内部数组的引用除外),但数组本身是值类型的数组(T[] )。具有 1000 个元素的 List&lt;MyStruct&gt;(其中 MyStruct 为 1000 个字节)将使用 1000000 个字节。这适用于外部列表,但不是“内部”列表。
    • @ReedCopsey 感谢您的更正,我已经更新了答案以反映值类型和引用类型之间的区别。
    【解决方案3】:

    这里有一篇关于这个主题的有趣帖子:

    http://blogs.msdn.com/b/joshwil/archive/2005/08/10/450202.aspx

    其中谈到了编写自己的“BigArray”对象。

    【讨论】:

      【解决方案4】:

      在 4.5 之前的 .NET 版本中,最大对象大小为 2GB。从 4.5 开始,如果启用了 gcAllowVeryLargeObjects,您可以分配更大的对象。请注意,string 的限制不受影响,但“数组”也应涵盖“列表”,因为列表由数组支持。

      【讨论】:

        【解决方案5】:
        class HugeList<T>
        {
            private const int PAGE_SIZE = 102400;
            private const int ALLOC_STEP = 1024;
        
            private T[][] _rowIndexes;
        
            private int _currentPage = -1;
            private int _nextItemIndex = PAGE_SIZE;
        
            private int _pageCount = 0;
            private int _itemCount = 0;
        
            #region Internals
        
            private void AddPage()
            {
                if (++_currentPage == _pageCount)
                    ExtendPages();
        
                _rowIndexes[_currentPage] = new T[PAGE_SIZE];
                _nextItemIndex = 0;
            }
        
            private void ExtendPages()
            {
                if (_rowIndexes == null)
                {
                    _rowIndexes = new T[ALLOC_STEP][];
                }
                else
                {
                    T[][] rowIndexes = new T[_rowIndexes.Length + ALLOC_STEP][];
        
                    Array.Copy(_rowIndexes, rowIndexes, _rowIndexes.Length);
        
                    _rowIndexes = rowIndexes;
                }
        
                _pageCount = _rowIndexes.Length;
            }
        
            #endregion Internals
        
            #region Public
        
            public int Count
            {
                get { return _itemCount; }
            }
        
            public void Add(T item)
            {
                if (_nextItemIndex == PAGE_SIZE)
                    AddPage();
        
                _itemCount++;
                _rowIndexes[_currentPage][_nextItemIndex++] = item;
            }
        
            public T this[int index]
            {
                get { return _rowIndexes[index / PAGE_SIZE][index % PAGE_SIZE]; }
                set { _rowIndexes[index / PAGE_SIZE][index % PAGE_SIZE] = value; }
            }
        
            #endregion Public
        }
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2015-11-24
          • 1970-01-01
          • 1970-01-01
          • 2016-04-27
          • 2014-05-26
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多