System.Array.IndexOf 分配内存

Posted

技术标签:

【中文标题】System.Array.IndexOf 分配内存【英文标题】:System.Array.IndexOf allocates memory 【发布时间】:2019-02-13 11:31:47 【问题描述】:

我一直在分析我的代码,发现System.Array.IndexOf 分配了相当多的内存。我一直在试图找出这是怎么发生的。

public struct LRItem

    public ProductionRule Rule  get;  // ProductionRule is a class
    public int Position  get; 


// ...

public List<LRItem> Items  get;  = new List<LRItem>();

// ...

public bool Add(LRItem item)

    if (Items.Contains(item)) return false;

    Items.Add(item);
    return true;

我假设IndexOfItems.Contains 调用,因为我认为Items.Add 没有任何业务检查索引。我试过查看reference source 和.NET Core source 但无济于事。这是 VS 分析器中的错误吗?这个函数实际上是在分配内存吗?我能以某种方式优化我的代码吗?

【问题讨论】:

在您看到 IndexOf 中的内存分配发生之前,您是否可能将列表的容量设置为某个较高的值? 检查LRItem 是如何实际实现Equals 的。罪魁祸首很可能就在那里。 另外,如果您需要一个强制唯一性的集合,请使用已经提供该功能的类型。 @elgonzo 不,我不会在任何地方设置或更改容量 @InBetween 我没有 LRItem 的自定义 Equals 【参考方案1】:

您的对象不是像 text 或 number 这样的简单数据类型,所以 检查复杂对象的每个项目可能是内存杀手。

例如,如果您的对象包含图像、文本、数字和...每个数据都应该进行相似性处理。

我建议您不要使用 IndexOf 或 Contains 函数,因为它们 可能比较对象中的每一个数据。

只需手动执行,使用单个 foreach 循环并只需比较您的密钥 数据(用户 ID、对象 ID、姓名、家庭、日期、时间或 ...)。

【讨论】:

好吧,再读一遍问题,然后看看你的答案。这是一些关于避免使用 IndexOf 以提高性能但与内存无关的有问题的建议。 是的,你是对的,在问题的最后,他要求优化!所以现在阅读我的答案,包含对复杂对象的调用导致过多的数据比较,从而导致时间、内存和性能问题。谢谢。 “你的对象不是文本或数字这样的简单数据类型”我认为你指的是值类型,而结构是值类型【参考方案2】:

我知道这可能有点晚了,但万一其他人有同样的问题......

List&lt;T&gt;.Contains(...) 被调用时,它使用EqualityComparer&lt;T&gt;.Default 来比较各个项目以查找您传入的内容[1]。 docs 这么说EqualityComparer&lt;T&gt;.Default

Default 属性检查类型 T 是否实现了 System.IEquatable 接口,如果是,则返回使用该实现的 EqualityComparer。否则,它返回一个使用 T 提供的 Object.Equals 和 Object.GetHashCode 覆盖的 EqualityComparer。

由于您的LRItem 没有 实现IEquatable&lt;T&gt;,因此它回退到使用Object.Equals(object, object)。而且因为LRItem 是一个结构,所以它最终会被装箱object,因此它可以传递给Object.Equals(...),这是分配的来源。

解决此问题的简单方法是从文档中获取提示并实现 IEquatable&lt;T&gt; 接口:

public struct LRItem : IEquatable<LRItem>

    // ...

    public bool Equals(LRItem other)
    
        // Implement this
        return true;
    

现在这将导致 EqualityComparer&lt;T&gt;.Default 返回一个专门的比较器,该比较器不需要对您的 LRItem 结构进行装箱,从而避免分配。

[1] 我不确定自从提出这个问题后是否发生了变化(或者可能是 .net 框架与核心差异之类的),但 List&lt;T&gt;.Contains() 现在不打电话给 Array.IndexOf()。无论哪种方式,他们都遵从EqualityComparer&lt;T&gt;.Default,这意味着这在任何一种情况下都应该是相关的。

【讨论】:

以上是关于System.Array.IndexOf 分配内存的主要内容,如果未能解决你的问题,请参考以下文章

Golang 1.14中内存分配、清扫和内存回收

c语言中啥是动态分配内存?

C内存分配

Linux 内核 内存管理伙伴分配器 ② ( 伙伴分配器分配内存流程 )

linux系统无法分配内存

内存分配