1

So I am writing an algorithm that plays kind of a board game against my classmates algorithm. It basically foresees every possible outcome of the game and chooses the best path based on win% of taking each path. In the game each player has 5 seconds to make his move. Since it all runs on one PC I cannot use processing power or too much memory space during my opponents move.

I am using a Tree structure with each Node looking like below:

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;
}

I am using a wrapper class Tree which containts the root of the tree and some methods for traversing and building the structure.

public class Tree {

    Node root;


    public Tree() {
        this.root = Node.createRoot();
    }

    //methods to traverse/build the tree
} 

Even after I've built the entire tree and chose my move the tree remains stored in the memory during my opponents move which is forbidden since he cannot make his move while I've taken up 4GB of RAM. I've tried setting the reference to null to trigger GC but it doesn't seem to work

} 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;
}

Since its all in one code block I have no further nor previous references to the tree structure.

How do I make garbage collector work or maybe free the memory manually?

Karol Dowbecki
  • 43,645
  • 9
  • 78
  • 111
Jan Chabik
  • 99
  • 1
  • 9
  • You may reduce the memory consumption dramatically by not repeating implied information in `Node` class. E.g. what is `Move`? Each move on a chess board can be encoded with just a starting point (64 possibilities) and an endpoint (63 possibilities). That requires just 12 bits, so a `short` would be sufficient. There’s also no need to keep a `boolean` to tell which player’s move it is. It’s alternating anyway, so every traversing code should already know that. But if you insist on it, the `short move` field still has spare bits. Then, do you really need `double` precision for `winPercentage`? – Holger Jan 14 '19 at 11:36
  • Likewise, traversing code can keep the back-link itself, so nodes do not need a `parent` field. And rethink, whether it really has to be a `Set` for the children. `Set` implementations like `HashSet` like to use more memory than their size for lookup efficiency. You’ll rarely need this (does your `Node` have a hashcode/equality at all?), so an array or an `ArrayList` (when calling `trimToSize()` after filling) would be more appropriate and need less memory. – Holger Jan 14 '19 at 11:43
  • To be honest the `parent` and `odd` fields are there because of my laziness. The game board is explained here [link](https://imgur.com/a/zrAko7e). The last person that is able to make a move wins. The board can be up to 51x51. Move has `equals()` and `hashcode` _Overriden_since {0;0},{0;1} is the same move as {0;1},{0;0}. It lets me search for all the available moves with ease. – Jan Chabik Jan 15 '19 at 12:07
  • When the order doesn’t matter, you’re better off with converting it to a canonical order when encoding, e.g. `p1=(x1<<6)|y1; p2=(x2<<6)|y2; if(p1>p2) { /* swap p1 & p2 */ } move = (p1 << 12)| p2;` This still fits into an `int` and has canonical equivalence (you only need `==`) and its value can be used as hash code too. Otherwise, you have `(51²)² == 6765201` distinct values for the `Move` and even more objects when you’re creating and storing them into the `Node`s without looking up already existing equivalent objects. We’re talking about a *minimum* of 100MB just for the `Move` objects… – Holger Jan 15 '19 at 14:03

1 Answers1

3

You can't trigger a GC yourself because System.gc() is just a hint. Doing tree = null; will remove the reference however the Tree object is still allocated in the heap until GC will decide to remove it.

Your best bet is to improve your code to not to require 4 GB of heap memory and constrain the JVM with -Xmx2g or similar option. As per this answer JVM is not keen on returning memory back to system. It can happen but it's not something that can be done every 5 seconds.

Karol Dowbecki
  • 43,645
  • 9
  • 78
  • 111