Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/303.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 使用Scala Case类处理GC上的重负载_Java_Scala_Garbage Collection_Case Class - Fatal编程技术网

Java 使用Scala Case类处理GC上的重负载

Java 使用Scala Case类处理GC上的重负载,java,scala,garbage-collection,case-class,Java,Scala,Garbage Collection,Case Class,我正在用Scala开发一个模拟游戏。我只想使用不可变对象来定义游戏逻辑,即使它效率低下。为什么?因为我能,所以我会 现在,我强调了一点主要的游戏循环,用巨大的负载推动模拟。基本上,我有这些嵌套的case类,并使用.copy()操作来定义模拟中给定实体的以下状态。问题是所有这些操作都会生成大量未引用的对象,因此在每一步结束时,我都会触发一个完整的GC。这对比赛的表现不利 所以我开始了一些积极的优化:首先,模拟步骤在游戏后面并行运行,计算“下一个状态”,基本上覆盖了游戏中的一天。因此,在计算下一天时

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

现在,我强调了一点主要的游戏循环,用巨大的负载推动模拟。基本上,我有这些嵌套的case类,并使用.copy()操作来定义模拟中给定实体的以下状态。问题是所有这些操作都会生成大量未引用的对象,因此在每一步结束时,我都会触发一个完整的GC。这对比赛的表现不利

所以我开始了一些积极的优化:首先,模拟步骤在游戏后面并行运行,计算“下一个状态”,基本上覆盖了游戏中的一天。因此,在计算下一天时,将向玩家显示当前一天的状态。这是因为基本上游戏中的每个主要实体(基本上是城市)都被认为是独立发展的。如果某个代理(玩家或其他在城市间旅行的AI代理)将与城市联系,我将根据代理操作的动作重新计算“下一个状态”。无论如何,这与现在无关

因此,我让这些平行实体在场景后面演化,当一天结束时(一天定义为玩家在世界地图中的5个步骤),我使用
wait.result(nextWorldState,5秒)
作为渲染点来更新模拟的当前状态。这不一定是游戏应该如何运行,但我想在游戏的其余部分等待计算下一个状态时测试rendez-vous点,因为这可能是必要的

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

假设我不想使用常规类(可能我会),假设我不想将状态拆分为多个部分并分别处理它们(我将尝试这样做,但它看起来无法维护),有什么聪明的解决方案吗

下面是一些处理此逻辑的实际函数的伪代码:

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)

    }
  }

}

为了减少完整GC的痛苦,我建议使用G1或CMS而不是并行收集器,并增加年轻空间以减少提升到永久空间的对象数量,但没有什么比首先创建更少的工作更重要的了


创建的垃圾越多,您所做的工作越多,gc就越需要完成清理对象的工作。用更多的CPU更快地创建对象并不会让GC变得更轻松。

事实上,创建大量短期不变的对象对于现代GC来说几乎是最好的例子。甚至在10年前,Azul的GC在一台864内核的机器上,使用Rich Hickey在Clojure中的蚂蚁模拟,在700个并行线程产生的20 GiByte/s的垃圾中耕耘,甚至不费吹灰之力。现代世代GCs建立在世代假设的基础上:对象在年轻时死亡,旧对象不引用年轻对象。这正是FP所做的:大量的短期对象,没有“向后的时间引用”。而且没有突变。@JörgWMittag 25 MB/s/core对那台机器来说太多了,但现在你可以让机器产生1 GB/s/core。调整gc最简单的方法就是将其置于较低的负载下。显然,我将致力于限制创建的对象数量,但现在我只想评估这种方法在重载情况下的可行性。无论如何,我尝试了CMS,并使用jstat进行了一些观察。结果是,CMS和默认GC之间的帧率差别很小,如果负载过大,它们都无法获得令人满意的性能。尽管如此,我还是学到了很多关于GC(从未以这种方式优化过)和我的代码的知识。关键是,只要我在内存中同时使用当前和下一个状态,我就会遇到问题。@Chobeat在java中,我让探查器指导我优化代码的区域。飞行记录器在这方面很好。我还没有用Scala试过。我用的是VisualVM,但感觉它不是一个合适的工具。谢谢你的建议,我一定会试试的。