I was talking with Ido about memory usage in AS3, and we discovered a small weakness in the AVM’s garbage collector. If you use a functional style of programming with lots of anonymous or nested functions, here’s something to watch out for:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
sprite will not be garbage collected, even though it’s just a local variable of
foo and no other references to it exist!
The issue is the anonymous function returned by
foo. This closure creates a reference to the outer scope,
foo. This is necessary so that you can access the outer variables from inside the nested function. For example, the inner function might have done
sprite.x = 10.
Unfortunately, outer variables are still referenced and stay alive, even if you never use them inside the closure! In our case, the free variable
sprite is still referenced by the closure even though it’s never used in the anonymous function. It remains ineligible for garbage collection. Notice that if we change the return to
return 0, then
sprite is collected and stops ticking.
Here are some tips to avoid this issue:
Ensure that closures will be garbage collected by clearing references to them. If we eventually do
func = null, then our closure is no longer reachable and will be collected, along with
Avoid using nested functions and anonymous functions. Instead of using an anonymous inner function, we could move the function outside:
1 2 3 4 5 6
- Null out any local variables at the end of the outer function. We can set
sprite = nullat the end of the function since we won’t be using it any longer.
This isn’t a big issue, but it’s something to keep in mind if you’re juggling function references with chunky objects like
BitmapData. Is it possible for a VM to be smart enough to reference only the objects that are actually needed, instead of pushing the entire outer scope? I’m not sure. I wonder if other VMs such as V8 handle this any better?