超出GC开销限制 - 数组

时间:2017-04-10 20:52:10

标签: java arrays garbage-collection

我在等待很长时间执行代码并将其指向此方法后得到此错误

public Iterable<Board> neighbors() {
        Queue<Board> q = new LinkedList<>();
        int n = dimension();
        int x = 0, y = 0;
        outer:
        // do some stuff to get the x and y
        if (y+1 < n) {
            the line where i get the error -> int [][]arr = new int[n][n];
            for (int i = 0; i < tiles.length; i++) {
                arr[i] = Arrays.copyOf(tiles[i], n);
            }
            // do some stuff
            Board br = new Board(arr);
            if(!this.equals(br)) {
                q.add(new Board(arr));
            }
        }
       if (y-1 >= 0) {
           int [][]arr = new int[n][n];
           for (int i = 0; i < tiles.length; i++) {
                arr[i] = Arrays.copyOf(tiles[i], n);
            }
            // do some stuff
            Board br = new Board(arr);
            if(!this.equals(br)) {
                q.add(new Board(arr));
            }
        }
       if (x-1 >= 0) {
           int [][]arr = new int[n][n];
           for (int i = 0; i < tiles.length; i++) {
                arr[i] = Arrays.copyOf(tiles[i], n);
            }
            // do some stuff
            Board br = new Board(arr);
            if(!this.equals(br)) {
                q.add(new Board(arr));
            }
        }
        if (x+1 < n) {
            int [][]arr = new int[n][n];
            for (int i = 0; i < tiles.length; i++) {
                 arr[i] = Arrays.copyOf(tiles[i], n);
             }
            // do some stuff
            Board br = new Board(arr);
            if(!this.equals(br)) {
                q.add(new Board(arr));
            }
        }
        return q;
    }

我基本上需要复制tiles数组并对副本“arr”进行更改,但保留tiles数组而不更改以便稍后使用..我真的不喜欢我正在做的方式复制和粘贴代码我认为它效率低但却没有其他方式出现在我的脑海里所以我想知道为什么我会得到这个错误“我知道它因为GC需要更多时间而不做很多”但我想知道为什么它在这种情况下也会发生,如果有更好的方法来复制数组。 还我将堆内存增加到-Xmx1600m

感谢您的时间。

1 个答案:

答案 0 :(得分:1)

问题

很可能是因为在短时间中创建了大量对象而产生了问题。有关详细信息,请参阅this answer

目前,一个Board至少包含四个对象:

  • 董事会本身
  • 主板内的数组arr
  • arr
  • 中的三个数组

创建更少的对象

我们的目标是创建更少的对象(数组)。由于您只想处理小型电路板,我们可以使用一个long来存储完整的3×3电路板。 long有64位。我们使用每个字段64/9 = 7位来存储该字段的值:

state = ... 0000100 0000011 0000010 0000001 0000000
           4th field   ↑   2nd field   ↑   0th field
                   3rd field       1st field

以下类处理位操作。

class Board {

    private final static int SIDE_LENGTH = 3;
    private final static int FIELDS = SIDE_LENGTH * SIDE_LENGTH;
    private final static int BITS_PER_FIELD = 64 / FIELDS;
    private final static long FIELD_MASK = (1 << BITS_PER_FIELD) - 1;

    private long state;

    public Board() {
        for (int field = 0; field < FIELDS; ++field) {
            set(field, field);
        }
    }

    /** Copy constructor. */
    public Board(Board other) {
        this.state = other.state;
    }

    public int get(int x, int y) {
        return get(coordinatesToField(x, y));
    }

    public void set(int x, int y, int value) {
        set(coordinatesToField(x, y), value);
    }

    private int coordinatesToField(int x, int y) {
        return SIDE_LENGTH * y + x;
    }

    private int get(int field) {
        return (int) ((state >>> (field * BITS_PER_FIELD)) & FIELD_MASK);
    }

    private void set(int field, int value) {
        int shift = field * BITS_PER_FIELD;
        state &= ~(FIELD_MASK << shift);
        state |= (long) value << shift;
    }

    public String toString() {
        StringBuilder sb = new StringBuilder();
        for (int field = 0; field < FIELDS; ++field) {
            sb.append(get(field));
            sb.append((field + 1) % SIDE_LENGTH == 0 ? "\n" : "\t");
        }
        return sb.toString();
    }

    // TODO implement equals and hashCode
}

使用这个类时,你不必再处理数组,这不仅可以保存很多对象,还可以保存你的prorgram中的复制代码。

该类也适用于1×1,2×2和4×4板,但由于64位限制,不适用于较大的板。

使用示例

public static void main(String[] args) {
    // Create and print the initial board
    // 0 1 2
    // 3 4 5
    // 6 7 8
    Board b = new Board();
    System.out.println(b);

    // Copy an existing board
    Bord copy = new Board(b);

    // Set the upper right field to value 8
    copy.set(2, 0, 8);

    // Print the center field
    // 4
    Syste.out.println(copy.get(1, 1));

}

其他想法

您甚至可以避免创建Board个对象,只存储long值。但是当你使用泛型(例如LinkedList)时,由于Java的自动装箱,这没有用。

另请注意,LinkedList将每个条目包装在其他节点对象中。也许您可以像循环缓冲区一样使用更高效的DataStructure。

根据您的工作情况,您可以查看Flyweight design pattern