在Java

时间:2015-05-05 22:02:17

标签: java arrays memory memory-management out-of-memory

我正在尝试生成一组应该(至少)为6x6的2D int数组。每个数组都存储0-6的值。我尝试使用简单的HashSet<int[][]>来存储它们(512MB的内存),我很快就收到了错误

Exception in thread "main" java.lang.OutOfMemoryError: GC overhead limit exceeded

只是进入该计划的一小段时间。

我为存储数组而考虑的选项:

  1. 将它们存储为基数为long。这仅适用于最多24(24.3717)个数字,因为long不能超过2^63位。< / p>

  2. 将它们存储为String(例如{{0, 0, 0, 1}, {3, 6, 2, 0}}将成为"00013620")。这只占用了4倍的空间(我认为),因为char仍然是1个字节。

  3. 使用BitSetBigInteger之类的内容?我不知道每个是什么或它们是如何工作的。

  4. 所以我的问题是: 从0到6存储6 x 6数值的最小方法是什么? 上面的选项是否有用,或者是否存在一个更简单的方法?

    注意:如果有必要,我可以使用8GB内存。

    我的代码(如果你必须知道,它与国际象棋有关): n是数组的大小(宽度和高度),应该能够达到(或过去)6。

    public static HashSet<int[][]> getBoards(int[][] data, int zero, int num) {
        HashSet<int[][]> ret = new HashSet<int[][]>(0);
    
        if (zero == num) {
            ret.add(data);
        } else if (zero == 0) {
            for (int y = 0; y < n; y++) {
                for (int x = 0; x < n; x++) {
                    for (int i = 1; i < 7; i++) {
                        int[][] d0 = new int[n][n];
                        d0[y][x] = i;
                        ret.addAll(getBoards(d0, 1, num));
                    }
                }
            }
        } else {
            for (int y = 0; y < n; y++) {
                for (int x = 0; x < n; x++) {
                    if (data[y][x] == 0) continue;
    
                    HashSet<int[]> moves = getMoves(data[y][x], x, y);
    
                    while (moves.iterator().hasNext()) {
                        int[] m = moves.iterator().next();
    
                        for (int i = 0; i < 6; i++) {
                            int[][] d0 = arrayCopy(data);
                            d0[m[0]][m[1]] = i;
    
                            ret.addAll(getBoards(d0, zero + 1, num));
                        }
                    }
                }
            }
        }
    
        return ret;
    }
    
    public static HashSet<int[]> getMoves(int piece, int xPos, int yPos) {
        HashSet<int[]> ret = new HashSet<int[]>(0);
    
        for (int y = 0; y < n; y++) {
            for (int x = 0; x < n; x++) {
                if (x == xPos && y == yPos) continue;
    
                switch (piece) {
                case 1:
                    if (y - yPos == 1 && Math.abs(x - xPos) == 1) ret.add(new int[] {y, x});
                    break;
                case 2:
                    if (Math.abs(y - yPos) + Math.abs(x - xPos) == 3 && x != xPos && y != yPos) ret.add(new int[] {y, x});
                    break;
                case 3:
                    if (Math.abs(y - yPos) == Math.abs(x - xPos)) ret.add(new int[] {y, x});
                    break;
                case 4:
                    if (y == yPos || x == xPos) ret.add(new int[] {y, x});
                    break;
                case 5:
                    if (Math.abs(y - yPos) == Math.abs(x - xPos) || y == yPos || x == xPos) ret.add(new int[] {y, x});
                    break;
                case 6:
                    if (Math.abs(y - yPos) <= 1 && Math.abs(x - xPos) <= 1) ret.add(new int[] {y, x});
                    break;
                default:
                    throw new IllegalArgumentException("Unknown Piece Number (" + piece + ")");
                }
            }
        }
    
        return ret;
    }
    

    完整错误:

    Exception in thread "main" java.lang.OutOfMemoryError: GC overhead limit exceeded
    at ChessGenerator.arrayCopy(ChessGenerator.java:120)
    at ChessGenerator.getBoards(ChessGenerator.java:71)
    at ChessGenerator.getBoards(ChessGenerator.java:74)
    at ChessGenerator.getBoards(ChessGenerator.java:74)
    at ChessGenerator.getBoards(ChessGenerator.java:74)
    at ChessGenerator.getBoards(ChessGenerator.java:74)
    at ChessGenerator.getBoards(ChessGenerator.java:74)
    at ChessGenerator.getBoards(ChessGenerator.java:74)
    at ChessGenerator.getBoards(ChessGenerator.java:56)
    at ChessGenerator.main(ChessGenerator.java:23)
    

    编辑:正如@Louis指出的那样,我对HashSet的使用导致了上述错误,但是,我仍然内存不足

    Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
    at ChessGenerator.arrayCopy(ChessGenerator.java:119)
    at ChessGenerator.getBoards(ChessGenerator.java:70)
    at ChessGenerator.getBoards(ChessGenerator.java:73)
    at ChessGenerator.getBoards(ChessGenerator.java:73)
    at ChessGenerator.getBoards(ChessGenerator.java:73)
    at ChessGenerator.getBoards(ChessGenerator.java:73)
    at ChessGenerator.getBoards(ChessGenerator.java:73)
    at ChessGenerator.getBoards(ChessGenerator.java:73)
    at ChessGenerator.getBoards(ChessGenerator.java:58)
    at ChessGenerator.main(ChessGenerator.java:23)
    

3 个答案:

答案 0 :(得分:1)

如果您希望HashSet仅保留唯一的int[][],并消除重复项,那将无效 - equalshashCode实施{ {1}}(和所有数组)都是基于身份的。如果你依赖于唯一性来保持不同数组的数量很小,那就无法工作了;你将不得不将它们包装在一个实现正确int[][]hashCode的类型中。

答案 1 :(得分:1)

你似乎在制造很多电路板,很难遵循,但似乎你基本上生成了大小为6X6的所有阵列的大部分,其中每个单元可以具有1,2,...的任何值, 6。

此类数组的数量为6 ^ 36~ = 10 ^ 28。

这意味着,即使每个数组只有一个字节(它不可能),你仍然需要10 ^ 16 TB才能保存所有数据。

我建议您寻找一种不包括明确生成所有可能数组的替代方案。

作为旁注,表示对象的最低位数是ceil(log_2(6^36)) = 94,但要获得最佳结果需要做很多工作,而且我赢了。建议它。

答案 2 :(得分:1)

最简单但仍具有内存效率的方法是将每个数组存储为两个long s,每个字段占用3位(总共3 * 36 = 108个有用位,开销为20未使用的位)。虽然理论上的限制小于此值,但您几乎肯定希望您的结构与字边界对齐,因此您并没有真正失去任何东西。你获胜的是,访问单个字段既简单又快速,只需要进行位屏蔽和移位操作。

我还会看看堆外存储选项,以消除所有对象开销。