【发布时间】:2019-06-07 23:21:31
【问题描述】:
所以我正在编写一个算法,它可以与我的同学算法进行棋盘游戏。它基本上预见了游戏的每一个可能结果,并根据走每条路径的胜率选择最佳路径。在游戏中,每个玩家有 5 秒的时间来移动。由于这一切都在一台 PC 上运行,因此我无法在对手移动时使用处理能力或过多的内存空间。
我正在使用一个树结构,每个节点如下所示:
public class Node {
//coordinates on the board
private Move move;
//winpercentage for choosing this path
private double winPercentage;
//is this move mine to make or is it opponents (the last person to make a move wins the game)
private boolean odd;
//all possible next moves
private Set<Node> children;
//previous move
private Node parent;
}
我正在使用一个包装类Tree,它包含树的根以及一些用于遍历和构建结构的方法。
public class Tree {
Node root;
public Tree() {
this.root = Node.createRoot();
}
//methods to traverse/build the tree
}
即使在我构建了整个树并选择了我的移动之后,树仍然存储在我的对手移动期间的内存中,这是被禁止的,因为当我占用了 4GB 的 RAM 时他无法移动。我尝试将引用设置为 null 以触发 GC,但它似乎不起作用
} else if(input.equals("tree")){
System.out.println("Showing tree structure");
Tree tree = new Tree();
//does all the work
tree.mainFunctionWrapper(tree.getRoot());
tree.setRoot(null);
tree = null;
}
由于它全部在一个代码块中,因此我没有进一步或以前对树结构的引用。
如何让垃圾收集器工作或手动释放内存?
【问题讨论】:
-
您可以通过不在
Node类中重复隐含信息来显着减少内存消耗。例如。Move是什么?棋盘上的每一步都可以只用一个起点(64 种可能性)和一个终点(63 种可能性)进行编码。这只需要 12 位,所以short就足够了。也没有必要保留boolean来告诉它是哪个玩家的动作。反正它是交替的,所以每个遍历代码都应该知道这一点。但是如果你坚持,short move字段仍然有多余的位。那么,对于winPercentage,您真的需要double精度吗? -
同样,遍历代码可以保留反向链接本身,因此节点不需要
parent字段。并重新考虑,是否真的必须为孩子们提供Set。Set像HashSet这样的实现喜欢使用比它们的大小更多的内存来提高查找效率。你很少需要这个(你的Node有哈希码/相等吗?),所以数组或ArrayList(在填充后调用trimToSize()时)会更合适并且需要更少的内存。跨度> -
老实说,
parent和odd字段是因为我的懒惰。游戏板在这里解释link。最后能够采取行动的人获胜。该板可最大为 51x51。 Move 具有equals()和hashcode_Overriden_since {0;0},{0;1} 与 {0;1},{0;0} 的移动相同。它让我可以轻松搜索所有可用的动作。 -
当顺序无关紧要时,最好在编码时将其转换为规范顺序,例如
p1=(x1<<6)|y1; p2=(x2<<6)|y2; if(p1>p2) { /* swap p1 & p2 */ } move = (p1 << 12)| p2;这仍然适合int并且具有规范等价性(您只需要==)并且它的值也可以用作哈希码。否则,当您创建并将它们存储到Nodes 中而不查找已经存在的等效对象时,您将拥有Move的(51²)² == 6765201不同值,甚至更多对象。我们说的是Move对象的最小为 100MB...
标签: java memory-management tree garbage-collection