4

我有一个在 JVM 上运行的应用程序(游戏)。

游戏的更新逻辑(运行 60 次/秒)以大约 25% 的“时间片”(1/60 秒)使用完成,然后在剩余的 75% 中休眠。但是当 GC 收集器开始运行时,它会上升到 75-200% 并在其余的执行过程中停留在那里。

游戏使用大约 70Mb 的堆并增长大约 1-2mb/s。当 GC 运行时,它会回到 70Mb,因此没有真正的内存泄漏。我将来会尝试降低这个数字,但在这个范围内应该不是问题。

我正在使用没有运行时参数或标志的 JVM 8,不确定哪个 GC 会给我。

我尝试将堆设置为不同的大小,但这不会影响这种现象。

关于为什么会这样,我有两种理论:

  1. GC 无意中将我的堆碎片化,导致更新循环中的缓存垃圾。我的逻辑从数据接近性中受益匪浅,因为它循环并更新它。会不会是把一些数据洗牌到老区,而把一些数据留在年轻区(托儿所)?

  2. 突然的 GC 处理触发了我的操作系统,使其意识到我的主要更新进程不需要像当前那样多的 CPU 资源,从而降低了它的优先级。(但是,即使我跳过 thread.sleep() 以休眠未使用的 CPU 使用率,这种现象仍然存在。

你怎么看。我的理论是否合理,可以对它们做些什么,还是我需要切换到 C 语言?我对GC的了解有限。

PS 作为旁注,通常 update() 在 GC 后完成 75%。当我得到像 200% 这样的数字时,它是在使用 VSync 时。

4

2 回答 2

1

不再有效:

我做了一个测试,完全毁了我的架构。我有这个,这是应用程序的瓶颈:

class Physics{
   Vec2 centre;
   Rec hitbox;
   Vec2 speed;
   Vec2 acc;
   ...

   public void update(){ //critical method
       centre.doThings();
       hitbox.doThings();
       etc...
   }

}

并将其更改为仅使用原语:

class Physics{
   double centreX,centreY;
   double x1,x2,y1,y2;
   double speedX,speedY;
   double accX,accY;
   ...

   public void update(){ //critical method
       implementation of methods above...
       etc...
   }

}

这是因为,至少,java 保证类基元成员按照声明的顺序存储在堆上的类头下方。虽然对对象的引用可以是堆另一侧的地址。

这与压缩 GC 一起给了我一个很好的提升,我认为这归功于缓存命中的增加。它摧毁了我的建筑,但这是我愿意付出的代价。

游戏现在以稳定的 15% 运行,现在我将标记我自己的帖子作为答案。

编辑: 那只是一个困惑的人的胡言乱语。以上只是给了我一个小小的性能提升——其余的都是由于应用程序中的一个错误,因此不能证明架构更改是合​​理的。不过,压缩 GC 有点帮助。

于 2016-11-18T17:34:49.233 回答
0

你怎么看。我的理论是否合理,

第一个理论是合理的,但第二个不是。

可以对他们做些什么吗

您可以通过以下方式改进:

  1. 增加最大堆大小。
  2. 切换到低暂停收集器。
  3. 基于分析应用程序的结果进行性能优化。
  4. 试图降低垃圾的产生率。

还是我需要切换到 C 或 C++?

C 和 C++ 将为您提供更可预测的行为,因为没有任何东西可以移动对象。如果你有相应的技能并付出努力,你应该能够在 C 和 C++ 中获得更好的性能,尤其是在做图形/渲染时。然而,这些都是很大的“如果”。

于 2016-11-18T09:51:07.840 回答