从内存中清除懒惰值

问题描述:

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

我唯一担心的是,最终,随着这些值被访问,对象将开始占用比真正需要更多的内存。

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

没有这样的机制。一旦访问了lazy val并且对其初始值设定项进行了评估,则结果值将保存在与其它任何其他实例相同的实例的字段中,并且只有该实例变为垃圾本身才会放弃对该“懒惰”值的引用,并可能)允许它被回收。当然,如果还有其他的引用,它们将阻止它被回收。

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

这取决于你是否有 - 我们称之为“弱值” - 对于不同类型或弱值的一种类型,但涉及许多对象。如果你有后者,我会使用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 

我觉得软(不弱)的引用这非常方便。弱引用会被每个不需要的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 

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

我建议你暂时忽略这个内存管理问题 - 不要使用懒惰的vals或soft引用或任何其他的释放机制。如果你的目标不是低内存环境(移动设备),那么你可以允许操作系统“交换”构造代码和大部分数据(在本地类型的字符串和数组中的数据)时间。

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