使用结构是可以的,使用 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 时,它们不是副本,而是引用,在我的代码的某些地方,这导致了一些不需要的副作用,最终导致无限循环,最终引发了内存不足错误.
是的,毕竟是追查问题,内存分析,做实验,找出某个变量值奇怪的原因。
感谢您的所有回答!
我正在经历一个相当棘手的问题,我不知道会发生什么,也不知道为什么会发生。我正在研究一个相当高强度的寻路算法 (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 时,它们不是副本,而是引用,在我的代码的某些地方,这导致了一些不需要的副作用,最终导致无限循环,最终引发了内存不足错误.
是的,毕竟是追查问题,内存分析,做实验,找出某个变量值奇怪的原因。
感谢您的所有回答!