【问题标题】:Unexpected memory usage of List<T>List<T> 的内存使用异常
【发布时间】:2012-04-22 16:47:35
【问题描述】:

我一直以为List的默认构造函数会初始化一个容量为4的list,加上第5个元素等时容量会翻倍……

在我的应用程序中,我制作了很多列表(树状结构,其中每个节点可以有很多子节点),其中一些节点不会有任何子节点,并且由于我的应用程序速度很快但也使用了太多内存,所以我决定使用可以指定容量的构造函数并将其设置为 1。

现在奇怪的是,当我以容量 1 开始时,内存使用量比我使用默认构造函数时高出大约 15%。这不可能是因为与 4 更匹配,因为加倍将是 1、2、4。那么为什么内存使用量会额外增加呢?作为一项额外的测试,我尝试从容量 4 开始。这一次,内存使用量再次比不使用指定容量时高 15%。

现在这确实不是问题,但困扰我多年的一个非常简单的数据结构有一些我还不知道的额外逻辑。有人知道 List 在这方面的内部工作原理吗?

【问题讨论】:

    标签: c#-4.0


    【解决方案1】:

    这是因为如果您使用默认构造函数,内部存储数组将设置为一个空数组,但如果您使用设置大小的构造函数,则会立即设置正确大小的数组,而不是在第一次调用时生成添加。

    您可以使用 JustDecompile 之类的反编译器看到这一点:

    public List(int capacity)
    {
        if (capacity < 0)
        {
            ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.capacity, ExceptionResource.ArgumentOutOfRange_NeedNonNegNum);
        }
        this._items = new T[capacity];
    }
    
    public List()
    {
        this._items = List<T>._emptyArray;
    }
    

    如果您查看 Add 函数,它会调用 EnsureCapacity,如果需要,它将扩大内部存储阵列。显然,如果数组最初设置为空数组,则第一次添加将创建默认大小的数组。

    【讨论】:

    • 啊,我想知道它是否会做这样的事情。有趣的是,空数组由该类型的所有列表共享。我很快就使用了这个技巧实现了我自己的 List。并确保增长只添加 1 个元素,现在我使用的内存减少了 8%,但速度仍然大致相同,再次感谢:D。
    猜你喜欢
    • 2013-12-22
    • 1970-01-01
    • 2013-03-17
    • 1970-01-01
    • 1970-01-01
    • 2013-05-26
    • 1970-01-01
    • 1970-01-01
    • 2012-01-11
    相关资源
    最近更新 更多