清除内存中的惰性值

时间:2013-03-16 03:23:31

标签: scala lazy-initialization

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

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

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

5 个答案:

答案 0 :(得分:7)

我发现软(非弱)引用非常方便。每个不需要的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)
}

现在您在Soft中包含适当数量的内容,如果需要,可以使用()获取数据:

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

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

val arr = soft()
// Intensive operations with arr

答案 1 :(得分:2)

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

答案 2 :(得分:1)

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

答案 3 :(得分:1)

这取决于你是否 - 让我们称之为“弱值” - 对于一种类型的不同类型或弱值,但涉及许多对象。如果你有后者,我会使用map / cache来使用Rogach的解决方案。

但是如果你有第一个 - 不同的类或者几个大对象 - 一种方法肯定是使用WeakReference

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

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)
}

输出btw是:

creating expensive object
This is expensive
This is expensive

答案 4 :(得分:1)

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

我建议您暂时忽略此内存管理问题 - 不要使用延迟val或软引用或任何其他释放方案。如果您不是针对低内存环境(移动设备),那么您可以允许操作系统“交换”构造代码和您在任何给定时未使用的大部分数据(字符串和本机类型数组中的数据)时间。

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