为什么 Dictionary.ContainsKey() & ToString() 导致 GC Alloc?

Why is Dictionary.ContainsKey() & ToString() causing GC Alloc?

除了有大约 1k 个实例是 运行 它们自己的 ContainsKey()ToString() 的事实之外,我无需提供更多细节。

Location 只是我对 Unity Vector3 的个人替代,以满足我的需要:

[Serializable] public struct Location
{
    public double X;
    public double Y;
    public double Z;

    public Location(double x, double y, double z) : this()
    {
        X = x;
        Y = y;
        Z = z;
    }

    public override string ToString()
    {
        return String.Format("{0}, {1}, {2}", X, Y, Z);
    }
}

(我知道我在使用 Structs 时违反了某种规则。只是不确定如何以另一种方式满足我的需求。)

这是分析器的屏幕截图运行:

如您所见,在时间线的大部分时间里,它是稳定的,然后在我的实例达到大约 1k(数量)后突然间,(它们从 100-250 开始)CPU 和内存由于似乎是 GC 分配而变得疯狂。我一直在寻找我可以更好地清理的东西,但我所看到的甚至导致任何 GC 分配是当我 运行 an:

if (_dictionary.ContainsKey(key)) {...}

并且在重命名 Unity 游戏对象时使用:

part.name = "Part: " + part.Location.ToString();

如果它只是与查找所花费的不可避免的时间有关,那么是否有任何替代 Dictionaries 的方法往往运行速度更慢但导致的 GC 分配更少,并且是否有更有效的方法来override ToString() 方法?

添加: 我的字典是关键:(我的个人结构)位置,值:Class 实例。

正在将评论变成答案...

您的 ToString() 方法总是会创建一个新字符串,所以这不足为奇。但是,您还使用了字符串连接,因此您创建了两个 个新字符串。您可以通过内联 ToString() 方法将其减少为一个。例如,为简洁起见,使用 C# 6 内插字符串:

var location = part.Location;
part.name = $"Part: {location.X}, {location.Y}, {location.Z}";

字典方面,有两个问题:

  • 您没有覆盖 EqualsGetHashCode,这 可能 意味着值被装箱以便调用 ValueType。我对此不是 100% 确定;拳击规则可能很复杂。
  • 您没有实施 IEquatable<T>,因此 非常 任何 Equals 调用都可能是拳击。

您可以轻松解决这两个问题:

[Serializable] public struct Location : IEquatable<Location>
{
    public double X;
    public double Y;
    public double Z;

    public Location(double x, double y, double z) : this()
    {
        X = x;
        Y = y;
        Z = z;
    }

    public override string ToString() => $"{X}, {Y}, {Z}";

    public override bool Equals(object obj) =>
        obj is Location loc && Equals(loc);

    public bool Equals(Location other) =>
        X == other.X && Y == other.Y && Z == other.Z;

    public override int GetHashCode()
    {
        // Replace with whatever implementation you want
        int hash = 17;
        hash = hash * 23 + X.GetHashCode();
        hash = hash * 23 + Y.GetHashCode();
        hash = hash * 23 + Z.GetHashCode();
        return hash;
    };
}

(这是使用 C# 7 语法,但我希望如果您使用现代版本的 Unity 和 VS2017,那仍然没问题。如果您使用的是旧版本,您应该能够实现相同的方法只是稍微冗长一点。)