使用结构是可以的,使用 class 会抛出 OutOfMemoryException(A* 算法)

Using a struct is OK, using a class throws OutOfMemoryException (A* algorithm)

我正在经历一个相当棘手的问题,我不知道会发生什么,也不知道为什么会发生。我正在研究一个相当高强度的寻路算法 (A*),并且我正在对这个结构进行大量的分配和操作:

public struct CellInfo
{
    public int MinCost;
    public CPos Path;
    public bool Seen;

    public CellInfo(int minCost, CPos path, bool seen)
    {
        MinCost = minCost;
        Path = path;
        Seen = seen;
    }
}

这个结构是可变的,因为我需要更新其中的值,所以我认为将它变成 class 是个好主意。但是,将 "struct" 简单更​​改为 "class" 会在应用程序启动后不久导致 OutOfMemoryException,我不知道为什么。有人可以阐明这个问题吗?

这是算法中最密集的部分,仅供参考:

public CPos Expand(IWorld world)
    {
        var currentMinNode = OpenQueue.Pop();
        while (CellInfo[currentMinNode.Location].Seen)
        {
            if (OpenQueue.Empty)
                return currentMinNode.Location;

            currentMinNode = OpenQueue.Pop();
        }

        var pCell = CellInfo[currentMinNode.Location];
        pCell.Seen = true;
        CellInfo[currentMinNode.Location] = pCell;

        // This current cell is ok; check all immediate directions:
        Considered.Add(currentMinNode.Location);

        var directions = GetNeighbors(currentMinNode.Location, pCell.Path);

        for (var i = 0; i < directions.Length; ++i)
        {
            var direction = directions[i];

            var neighborCPos = currentMinNode.Location + direction;

            // Is this direction flat-out unusable or already seen?
            // TODO: The "as Actor" is made to just isolate this clase, but in the future
            // everything should use IActor implementation instead of concrete class.
            if (!world.IMap.Contains(neighborCPos) ||
                CellInfo[neighborCPos].Seen ||
                !mobileInfo.CanEnterCell(world as World, Self as Actor, neighborCPos, IgnoredActor as Actor, CheckForBlocked ? CellConditions.TransientActors : CellConditions.None) ||
                (customBlock != null && customBlock(neighborCPos)))
                continue;

            var cellCost = CalculateCellCost(world, neighborCPos, direction);
            var gCost = CellInfo[currentMinNode.Location].MinCost + cellCost;

            // Cost is even higher; next direction:
            if (gCost > CellInfo[neighborCPos].MinCost)
                continue;

            // Now we may seriously consider this direction using heuristics:
            var hCost = Heuristic(neighborCPos);

            var neighborCell = CellInfo[neighborCPos];
            neighborCell.Path = currentMinNode.Location;
            neighborCell.MinCost = gCost;
            CellInfo[neighborCPos] = neighborCell;

            OpenQueue.Add(new PathDistance(gCost + hCost, neighborCPos));

            if (gCost > MaxCost)
                MaxCost = gCost;

            Considered.Add(neighborCPos);
        }

        // Sort to prefer the cheaper direction:
        // Array.Sort(nextDirections, (a, b) => a.Second.CompareTo(b.Second));
        return currentMinNode.Location;
    }

我有过类似的经历,我找到了以下细节。结构实例是在堆栈上创建的,但是 class 实例是在堆上创建的,垃圾收集器使用结构清除内存的每个部分比使用 class 更快。问题的另一个方面是 class 实例比结构有更多的内存。例如这里 http://www.codeproject.com/Articles/231120/Reducing-memory-footprint-and-object-instance-size mentiones that Object has a 12 bytes and + 4 possibly unused bytes depending on your class (I don't know purpose of padding). As a way of solution, I propose you extensive idea ( http://bhrnjica.net/2012/07/22/with-net-4-5-10-years-memory-limit-of-2-gb-is-over/ ),或者你可以修改你的算法并避免内存中的大数据数组

我找到了解决问题的办法。当它是一个结构时,每次我传递变量时它们都是副本而不是引用,所以我创建的每个由 CellInfo 组成的新数据结构都是全新的。

当更改为 class 时,它们不是副本,而是引用,在我的代码的某些地方,这导致了一些不需要的副作用,最终导致无限循环,最终引发了内存不足错误.

是的,毕竟是追查问题,内存分析,做实验,找出某个变量值奇怪的原因。

感谢您的所有回答!