1

我现在正在编写一个 JRPG 风格的游戏,并在 YAML 文件中定义我的项目/敌人等。我决定将它们作为惰性值预编译到 Scala 对象中,而不是在运行时加载它们(这在 Scala 中被证明是一个痛苦,尤其是在 Android 上)。

我唯一担心的是,最终,当访问这些值时,对象将开始占用比实际需要更多的内存。

无论如何要重新初始化 Scala 对象或将惰性值清除回默认状态?或者,有没有更好的方法来完成我在这里尝试做的事情?

4

5 回答 5

8

我发现软(不弱)引用对此非常方便。每次不需要它们的 GC 都会吃掉弱引用,如果重复访问它们会浪费很多精力。软引用仅在存在内存压力时才会被吃掉(这可能是每次 GC,但至少 JVM 可以行使一点自由裁量权)。无论如何,对于 Scala 使用,这非常方便:

class Soft[T,U](t: T)(gen: T => U) {
  private[this] var cache = new java.lang.ref.SoftReference(gen(t))
  def apply(): U = {
    var u = cache.get()
    if (u==null) {
      u = gen(t)
      cache = new java.lang.ref.SoftReference(u)
    }
    u
  }
}
object Soft {
  def apply[T,U](t: T)(gen: T => U) = new Soft(t)(gen)
}

现在您将适量的东西包装在 a 中Soft,并在需要时使用它()来获取数据:

val soft = Soft(10)(n => Array.tabulate(n)(i => i*i*i))
soft()(3)   // 27

获得软引用(通常等于创建几个对象)有一个不可完全忽略的惩罚,所以如果您要大量使用某些东西,请先抓住它,然后再做工作:

val arr = soft()
// Intensive operations with arr
于 2013-03-16T15:15:20.697 回答
2

没有这样的机制。一旦lazy val访问 a 并评估其初始化程序,结果值将保存在实例的字段中,它与任何其他实例一样,只有该实例成为垃圾本身才会放弃对该“惰性”值的引用,并且(可能) 也允许它被回收。自然,如果有其他引用它,它们将阻止它被回收。

于 2013-03-16T04:08:53.617 回答
1

我认为它没有内置机制,因此您需要自己实现它。最简单的解决方案是在经过一定时间后拥有某种形式的过期缓存 - 例如,如Java time-based map/cache with expiring keys中所述。

于 2013-03-16T06:19:06.093 回答
1

这取决于您是否拥有 - 我们称它们为“弱值” - 用于不同类型或一种类型但涉及许多对象的弱值。如果你有后者,我会选择 Rogach 使用地图/缓存的解决方案。

但是,如果您有第一个不同的类或一些大对象,那么一种方法肯定是使用Wea​​kReference

这是我想出的一个解决方案——也许将来可以用宏来完成:

object UseWeakRef extends App {

  import scala.ref.WeakReference

  class WeakRefCreator[T <: AnyRef] {
    private var weakRef: WeakReference[T] = WeakReference(null.asInstanceOf[T])
    def apply(creator: => T): T = weakRef.get match {
      case None =>
        val newVal: T = creator
        weakRef = WeakReference(newVal); newVal
      case Some(value) => value
    }
  }

  private val expensiveRef = new WeakRefCreator[String]
  def expensiveVal = expensiveRef {
    println("creating expensive object")
    "This is expensive"
  }

  println(expensiveVal)
  println(expensiveVal)
}

顺便说一句,输出是:

creating expensive object
This is expensive
This is expensive
于 2013-03-16T06:29:59.540 回答
1

通过使用代码生成方法,您将始终拥有至少一个以代码形式构建对象的对象副本,并且可能在对象本身中拥有另一个副本。构造数据的代码可能比实际对象使用更多的内存。

我建议你暂时完全忽略这个内存管理问题——不要使用惰性 vals 或软引用或任何其他释放方案。如果您的目标不是低内存环境(移动设备),那么您可以允许操作系统“交换”您在任何给定情况下都不使用的构造代码和大部分数据(字符串和原生类型数组中的数据)时间。

由于您仍然拥有原始 YAML 文件,因此如果您发现游戏的这一部分使用了太多内存,您可以在游戏制作的润色/优化阶段恢复从数据文件加载。

于 2013-05-20T18:22:43.080 回答