2013-03-16 5 views
1

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

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

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

ответ

2

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

1

Я думаю, что для этого нет встроенного механизма, поэтому вам нужно реализовать его самостоятельно. Простейшим решением было бы иметь некоторую форму кеша с истечением срока действия через определенное время - например, как описано в Java time-based map/cache with expiring keys.

1

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

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

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

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

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

Смежные вопросы