java - 使用 Scala 案例类处理 GC 上的重负载

标签 java scala garbage-collection case-class

我正在游戏中用 Scala 开发模拟。我想只使用不可变的对象来定义游戏逻辑,即使它效率低下。为什么?因为我能,所以我会。

现在我要强调一下主游戏循环,并以巨大的负载插入模拟。基本上我有这些嵌套的案例类,并且我使用 .copy() 来定义模拟内给定实体的以下状态。问题是所有这些操作都会生成大量未引用的对象,因此在每个步骤结束时,我都会触发 Full GC。这对游戏性能不利。

所以我开始了一些积极的优化:首先,现在模拟步骤在游戏后面并行运行,计算基本上涵盖游戏中一天的“下一个状态”。因此,在计算下一天的同时,玩家将看到当天的状态。这是有效的,因为基本上游戏中的每个主要实体(基本上是城市)都被假设是孤立发展的。如果某个代理(玩家或其他在城市之间旅行的 AI 代理)与该城市取得联系,我将根据代理操作的操作重新计算“下一个状态”。无论如何,这现在已经不相关了。

因此,我让这些并行实体在幕后演变,当一天结束时(一天定义为玩家在世界地图中的 5 步),我使用 Await.result(nextWorldState, 5 秒) 作为集合点来更新模拟的当前状态。这不一定是游戏应该如何运行,但我想在游戏的其余部分等待计算下一个状态时测试交汇点,因为这可能是必要的。

我的问题是,在此之后,我一次取消引用很多对象,这会触发 GC 收集。我尝试了一些解决方案,但总有一个点当我访问 Future 的结果并用它替换当前状态时,这总是会触发 GC。

假设我不想参加常规类(class)(可能我会)并假设我不想将状态分成多个部分并单独处理它们(我会尝试这个,但它看起来无法维护),有什么巧妙的方法可以解决这个问题吗?

下面是来自处理此逻辑的实际函数的一些不那么伪的代码:

class WorldSimulationManager(var worldState: WorldState) {

  private var nextWorldState: Future[WorldState] =
    Future.successful(worldState)


  def doImmutableStep(gameMap:GameMap,movedCountToday:Int):Unit = {

    nextWorldState =
      nextWorldState.flatMap(_.doStep(gameMap))

    if (movedCountToday >= Constants.tileMovementsToDay) {
      worldState = Await.result(nextWorldState, 5 seconds)

    }
  }

}

最佳答案

为了减少 Full GC 的痛苦,我建议使用 G1 或 CMS 而不是并行收集器,并增加年轻空间以减少升级到永久空间的对象数量,但是没有什么比从一开始就减少工作量更好的了。

你创建的垃圾越多,你做的工作就越多,GC 清理对象的工作也就越多。使用更多 CPU 更快地创建对象不会减轻 GC 的痛苦。

关于java - 使用 Scala 案例类处理 GC 上的重负载,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41750947/

相关文章:

java - 类类型变量或在构造函数/方法中实例化之间有什么区别

java - Wildfly 热部署太慢

scala - 使用包含 self 类型的 Prop 创建 Actor

java - 如何清理无休止运行的线程?

java - informix临时表的唯一ID是什么?

java - OSGI - 处理 bundle 所需的第 3 方 JAR

scala - cons 是方法还是类?

performance - 有什么方法可以提高 PySpark 输出的效率吗?

c# - 什么时候需要 Dispose?

java - 并行垃圾收集器是否与主程序一起在单独的线程上运行