Java 保存二维图块:for 循环在每次迭代中变慢,最多 5 秒,迭代 10000 次
Java saving 2d tiles: for loop getting slower with each iteration up to 5 seconds for 10000 iterations
我目前正在开发一款基于图块的 2d 游戏。目前,我正在编写一个程序来拯救整个世界。一般来说,一个世界有 10000 x 10 = 1.100.000 个方块大。显然,这对 java 应该不是问题,但它是。
这是我的循环:
Material[][] blocks = g.gameState.getLevel().tiles;
int y = 0;
int blocksHor = g.gameState.getLevel().getBlocksHor();
for(int x = 0; x < blocksHor; x++) {
world += blocks[y][x] + " ";
if(x >= blocksHor - 1) {
x = 0;
y++;
System.out.println("LAYER " + y + " => " + (System.currentTimeMillis() - now) + " ms");
now = System.currentTimeMillis();
}
}
循环有点难懂,但我已经尽力优化了。这是控制台输出:
LAYER 1 => 86 ms
LAYER 2 => 276 ms
LAYER 3 => 424 ms
LAYER 4 => 618 ms
LAYER 5 => 561 ms
LAYER 6 => 541 ms
LAYER 7 => 755 ms
LAYER 8 => 885 ms
LAYER 9 => 1036 ms
这些过程结束时最多需要 50 秒或更多。
这个循环有什么问题?老实说,我不知道该如何解决这个问题。
谢谢你:)
-菲尔
如果您提供的代码确实代表了您的问题,那么问题很可能出现在
world += blocks[y][x] + " ";
这有几个性能问题。首先,world
必须具有类型 String
,并且 String
是不可变的。因此,无论何时通过 +
运算符连接它们,都必须创建一个新的来保存结果。您在该语句的每次执行中至少执行两次,产生了最终必须收集的总计超过 2M 的垃圾对象。
此外,world
的中间值的大小稳步增加,因此所有这些临时对象消耗的组合内存与 平方 的总数成正比块。即使每个块的字符串值只有一个字符长,这也会占用大量内存。你最终会遇到严重的垃圾收集问题,而且随着你的继续,它们确实会变得更糟。
作为改进这一点的第一次尝试,您可以考虑将数据累积到 StringBuilder
而不是 String
:
StringBuilder worldBuilder = new StringBuilder();
// ...
worldBuilder.append(blocks[y][x]).append(' ');
这应该会通过产生更少的垃圾来显着缓解 GC 问题,但您最终仍将需要一个相当大的对象来保存整个状态。因此,我还建议将数据累积成更小的块,边走边输出,而不是在输出任何内容之前在内存中构建整个表示。
您可能还想查看 Material.toString()
的实现,因为如果它也通过执行字符串连接产生大量垃圾,那么这将放大问题。
我目前正在开发一款基于图块的 2d 游戏。目前,我正在编写一个程序来拯救整个世界。一般来说,一个世界有 10000 x 10 = 1.100.000 个方块大。显然,这对 java 应该不是问题,但它是。
这是我的循环:
Material[][] blocks = g.gameState.getLevel().tiles;
int y = 0;
int blocksHor = g.gameState.getLevel().getBlocksHor();
for(int x = 0; x < blocksHor; x++) {
world += blocks[y][x] + " ";
if(x >= blocksHor - 1) {
x = 0;
y++;
System.out.println("LAYER " + y + " => " + (System.currentTimeMillis() - now) + " ms");
now = System.currentTimeMillis();
}
}
循环有点难懂,但我已经尽力优化了。这是控制台输出:
LAYER 1 => 86 ms
LAYER 2 => 276 ms
LAYER 3 => 424 ms
LAYER 4 => 618 ms
LAYER 5 => 561 ms
LAYER 6 => 541 ms
LAYER 7 => 755 ms
LAYER 8 => 885 ms
LAYER 9 => 1036 ms
这些过程结束时最多需要 50 秒或更多。
这个循环有什么问题?老实说,我不知道该如何解决这个问题。
谢谢你:) -菲尔
如果您提供的代码确实代表了您的问题,那么问题很可能出现在
world += blocks[y][x] + " ";
这有几个性能问题。首先,world
必须具有类型 String
,并且 String
是不可变的。因此,无论何时通过 +
运算符连接它们,都必须创建一个新的来保存结果。您在该语句的每次执行中至少执行两次,产生了最终必须收集的总计超过 2M 的垃圾对象。
此外,world
的中间值的大小稳步增加,因此所有这些临时对象消耗的组合内存与 平方 的总数成正比块。即使每个块的字符串值只有一个字符长,这也会占用大量内存。你最终会遇到严重的垃圾收集问题,而且随着你的继续,它们确实会变得更糟。
作为改进这一点的第一次尝试,您可以考虑将数据累积到 StringBuilder
而不是 String
:
StringBuilder worldBuilder = new StringBuilder();
// ...
worldBuilder.append(blocks[y][x]).append(' ');
这应该会通过产生更少的垃圾来显着缓解 GC 问题,但您最终仍将需要一个相当大的对象来保存整个状态。因此,我还建议将数据累积成更小的块,边走边输出,而不是在输出任何内容之前在内存中构建整个表示。
您可能还想查看 Material.toString()
的实现,因为如果它也通过执行字符串连接产生大量垃圾,那么这将放大问题。