简体   繁体   中英

Scala: lazy vals, call by name, closures and memory leaks

I have a scala procedure creating a large data structure using an even larger index in the process. Because I want to do it in one pass and not get boggled down in complicated precedence resolution, I'm using lazy vals in the result initialized with expressions which might not evaluate to a correct value (or any at all) at the moment of creating the component, but will do so once the whole build process is completed. This means that each component of the final result has a synthetic reference to a closure with my whole index, and potentially, as long as any of them is still in memory, my index cannot be garbage collected. Obviously, I don't want it - ideally I'd like to be able to make a second pass over the structure to initialize the values if needed (and to ensure that any errors are caught at this point), and let the index be garbage collected. Currently I pass the the initialization expression by name through several functions and use it in a lazy val declaration, equivalent to this:

class Component(init : =>Component) {
   lazy val property = init
}
...
new Component(index.get(parameters))

Is this sound? Will the synthetic init field by dereferenced once lazy val is accessed? What if I want to use it in an initialization function, like this:

class Component(init : =>Component) {
   private def evaluate = init
   lazy val property = evaluate
}

Are there any rules to keep in mind in general when programming with closures?

The main problem you're describing--that the index can't be garbage collected--is solved by putting the index into a mutable box that you empty (null out) once the object is created.

However, if you don't know when your object is created, and need the program to tell you (eg by knowing that all lazy vals have been populated), then you're out of luck. Barring poking around in memory with sun.misc.Unsafe , you're not supposed to know those sorts of details. (That's kind of the point of lazy vals.)

You could cook up a reference-counting scheme that would help you somewhat at detecting yourself when you can clear the box: increment a counter on the box when you enter the constructor, keep a private field count of how many lazy vals you have, and decrement that count (atomically!) every time you initialize a lazy val and if you hit zero, decrement the counter on the box and null the box if the box counter has reached zero.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM