一尘不染

如何避免Java游戏中垃圾回收的延迟?(最佳做法)

java

我正在针对Java平台在Java中优化交互式游戏的性能。有时会在绘图和交互方面进行垃圾收集工作。通常小于一秒的十分之一,但有时在非常慢的设备上可能长达200毫秒。

我正在使用ddms事件探查器(Android SDK的一部分)来搜索我的内存分配来自何处,并从内部绘图和逻辑循环中删除它们。

最严重的罪犯是短循环,例如

for(GameObject gob : interactiveObjects)
    gob.onDraw(canvas);

每次执行循环时,都会iterator分配一个。我现在ArrayList为我的对象使用数组()。如果我想在内部循环中使用树或哈希,我知道我需要小心甚至重新实现它们,而不是使用Java
Collections框架,因为我负担不起额外的垃圾回收。当我查看优先级队列时,可能会出现这种情况。

我也想在显示分数和使用进度时遇到麻烦Canvas.drawText。这不好,

canvas.drawText("Your score is: " + Score.points, x, y, paint);

因为Stringschar数组和StringBuffers将全部分配以使其工作。如果您有几个文本显示项目,并且每秒运行60次框架,这将开始累加,这会增加垃圾收集的麻烦。我认为最好的选择是保留char[]数组并解码intdouble手动将其解码,然后将字符串连接到开头和结尾。我想听听是否有清洁剂。

我知道那里肯定还有其他人在处理这个问题。您如何处理它,发现在Java或Android上交互运行的陷阱和最佳实践是什么?这些gc问题足以让我错过手动内存管理功能,但不是很多。


阅读 210

收藏
2020-09-08

共1个答案

一尘不染

我对Java的手机游戏合作......以避免GC’ing对象(这反过来,最好的办法 在一个点触发GC或其他和
杀死你的游戏的perfs)仅仅是为了避免在主游戏创建它们首先循环。

没有“干净”的方法可以解决这个问题,我先举一个例子。

通常,您在屏幕上有4个球,分别为(50,25),(70,32),(16,18),(98,73)。好吧,这是您的抽象(为方便起见,将其简化):

n = 4;
int[] { 50, 25, 70, 32, 16, 18, 98, 73 }

您“弹出”消失的第二个球,您的int []变为:

n = 3
int[] { 50, 25, 98, 73, 16, 18, 98, 73 }

(请注意,我们什至根本不关心“清洁”第四个球(98,73),我们只是跟踪剩下的球的数量)。

不幸的是,手动跟踪对象。这是在移动设备上最新的性能良好的Java游戏中完成的。

现在,对于字符串,这是我要做的:

  • 在游戏初始化时, 仅在 将数字0到9保存在数组中之后 使用 drawText(…)进行 预绘制。 __BufferedImage[10]
  • 在游戏初始化时,预先绘制一次 “您的分数是:”
  • 如果 “您的分数是:” 确实需要重绘(因为它是透明的),则从您的预存中重绘BufferedImage
  • 循环以计算分数的位数,并在 “您的分数为:”之后 ,将每个数字手动一个接一个地添加(每次从您BufferedImage[10]预先存储的位置复制对应的数字(0到9))。

这让您 两全其美 :您可以重用 drawtext(…) 字体,并且在主循环中创建了完全为零的对象(因为您 回避了对
drawtext(…) 的调用,这本身很 可能 是糟糕地产生不必要的废话)。

这个 “零对象创建绘图分数”的 另一个“好处” 是,仔细的图像缓存和字体的重用并不是真正的 “手动对象分配/重新分配” ,而是真正的仔细缓存。

这不是“干净的”,也不是“好的做法”,但这是在一流的手机游戏(例如Uniwar)中的实现方式。

而且速度很快。快点 比涉及对象创建的 任何事物 都要快。

PS:实际上,如果您仔细看一些手机游戏,您会发现字体实际上不是系统/
Java字体,而是专门为每个游戏制作的像素完美字体(在这里,我仅向您提供了有关如何缓存系统的示例/
Java字体,但显然您也可以缓存/重复使用像素完美/位图字体)。

2020-09-08