Удаление ленивого значения из памяти

В данный момент я пишу игру в стиле JRPG и определяю свои предметы/врагов и т. д. в файлах YAML. Вместо того, чтобы загружать их во время выполнения (что является проблемой для Scala, особенно для Android), я решил предварительно скомпилировать их в объект Scala как ленивые значения.

Единственное, что меня беспокоит, это то, что в конце концов при доступе к этим значениям объект начнет занимать больше памяти, чем действительно необходимо.

Есть ли способ повторно инициализировать объект Scala или очистить ленивые значения до состояния по умолчанию? В качестве альтернативы, есть ли лучший способ выполнить то, что я пытаюсь сделать здесь?


person Mistodon    schedule 16.03.2013    source источник


Ответы (5)


Я нахожу мягкие (не слабые) ссылки очень удобными для этого. Слабые ссылки съедаются каждый 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
person Rex Kerr    schedule 16.03.2013

Нет такого механизма. После обращения к lazy val и оценки его инициализатора результирующее значение сохраняется в поле экземпляра, частью которого оно является, как и любое другое, и только тот экземпляр, который сам становится мусором, отказывается от ссылки на это «ленивое» значение и ( возможно) также разрешить его восстановление. Естественно, если есть другие упоминания о нем, они исключат возможность его истребования.

person Randall Schulz    schedule 16.03.2013

Я думаю, что для этого нет встроенного механизма, поэтому вам нужно будет реализовать его самостоятельно. Самым простым решением было бы иметь некоторую форму кеша с истечением срока действия по прошествии определенного времени, например, как описано в временная карта/кеш Java с ключами с истекающим сроком действия.

person Rogach    schedule 16.03.2013

Это зависит от того, есть ли у вас - назовем их "слабыми значениями" - для разных типов или слабых значений одного типа, но много задействованных объектов. Если у вас есть последнее, я бы выбрал решение Рогача, используя карту/кеш.

Однако, если у вас есть первое - разные классы или, может быть, несколько больших объектов - один из подходов, безусловно, заключается в использовании 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)
}

Выход между прочим:

creating expensive object
This is expensive
This is expensive
person michael_s    schedule 16.03.2013

При использовании подхода генерации кода у вас всегда будет по крайней мере одна копия объектов в виде кода для создания объекта и, возможно, еще одна копия в самих объектах. Код для создания данных, вероятно, использует больше памяти, чем сами объекты.

Я предлагаю вам какое-то время полностью игнорировать эту проблему управления памятью — не используйте ленивые vals, программные ссылки или любые другие схемы освобождения памяти. Если вы не ориентируетесь на среды с малым объемом памяти (мобильные устройства), вы можете разрешить операционной системе «заменять» код построения и большую часть данных (данные в строках и массивах собственных типов), которые вы не используете в данный момент. время.

Поскольку у вас все еще есть исходные файлы YAML, вы можете вернуться к загрузке из файлов данных на этапе полировки/оптимизации производства игры, если обнаружите, что эта часть игры использует слишком много памяти.

person Dobes Vandermeer    schedule 20.05.2013