开发者

AS3 - when does the garbage collector run?

apologies if this is a dupe; i couldn't find it.

i've read and understood grant skinner's blog on the AS3 garbage collector - http://www.adobe.ca/devnet/flashplayer/articles/garbage_collection.html , but my question isn't covered there.

here's my question.

suppose i've written some AS3 code like:

statementOne;
statementTwo;

is there any possibility that the garbage collector will run during or between my two statements, or does it o开发者_高级运维nly run after my "user" code has finished and returned control up to flash ?

we have an A-Star codeblock that's sometimes slow, and i'd like to eliminate the GC as a potential culprit. the codeblock is obviously more complex than my example above, but it doesn't involve any events or other asynchronous stuff.

tia, Orion


The garbage collector isn't threaded, so generally speaking it won't run while your code is running. There is one exceptional case where this isn't true, however.

The new keyword will invoke the garbage collector if it runs out of memory.

You can run this code to observe this behaviour:

var vec:Vector.<*> = new Vector.<*>(9001);
for (;;) {
    vec[0] = new Vector.<*>(9001);
    vec = vec[0];
}

Memory usage will quickly jump up to the maximum (1GB for me) then hold until the time cutoff.


There are many good answers here but I think a few subtleties have not been addressed.

  1. The Flash player implements two kinds of garbage collection. One is reference counting, where Flash keeps a count of the incoming references to each object, and knows that when the count reaches zero the object can be freed. The second method is mark-sweep, where Flash occasionally searches for and deletes isolated groups of objects (where the objects refer to one another, but the group as a whole has no incoming references).
  2. As a matter of specification either kind of garbage collection may occur at any time. The player makes no guarantee of when objects will be freed, and mark-sweeps may even be spread out over several frames. Further, there's no guarantee that the timing of GCs will stay the same between Flash player versions, or platforms, or even browsers. So in a way, it's not very useful to know anything about how GCs are triggered, as you have no way of knowing how widely applicable your knowledge is.
  3. Previous point aside, generally speaking mark-sweeps are infrequent, and only triggered in certain circumstances. I know of certain player/OS configurations where a mark/sweep could be triggered every N seconds, or whenever the player exceeded P% of the total available memory, but this was a long time ago and the details will have changed. Reference-counting GCs, in contrast, can happen frequently - between frames, at least. I don't think I've seen them demonstrated to happen more frequently, but that doesn't mean it can't occur (at least in certain situations).

Finally, a word about your specific issue: in my experience it's very unlikely that the GC has anything to do with your performance problem. Reference-counted GC processing may well occur during your loops, but generally this is a good thing - this kind of collection is extremely fast, and keeps your memory usage manageable. The whole point of managing your references carefully is to ensure that this kind of GC happens in a timely manner.

On the other hand, if a mark/sweep occurs during your loops, that would certainly affect your performance. But this is relatively hard to mistake and easy to test. If you're testing on a PC and your application doesn't regularly climb into the hundreds of MBs of memory usage (test with System.totalMemory), you're probably not getting any mark/sweeps at all. If you do have them, it should be easy to verify that the associated pauses are large, infrequent, and always accompanied by a big drop in memory usage. If those things aren't true, you can disregard the idea that GC is part of your problems.


To my knowledge, this is not documented. I have a feeling the GC won't run while your code is being executed (that is, while your code is on the execution stack; each frame, the player creates a new stack for use code). Obviously, this comes from observation and my own experience with Flash, so I wouldn't say this is 100% accurate. Hopefully, it's an educated guess.

Here's a simple test that seems to show that the above is true:

package {
    import flash.display.Sprite;
    import flash.net.FileReference;
    import flash.system.System;
    import flash.utils.Dictionary;
    import flash.utils.setTimeout;

    public class test extends Sprite
    {

        private var _dict:Dictionary = new Dictionary(true);

        public function test()
        {
            testGC();
            setTimeout(function():void { 
                traceCount();
            },2000);            
        }

        private function testGC():void {
            var fileRef:FileReference;
            for(var i:int = 0; i < 100; i++) {
                fileRef = new FileReference();
                _dict[fileRef] = true;
                traceCount();
                System.gc();
            }
        }

        private function traceCount():void {
            var count:int = 0;
            for(var i:* in _dict) {
                count++;
            }
            trace(count);
        }

    }
}

The GC seems to be particularly greedy when there are FileReference objects involved (again, this is from my experience; this isn't documented as far as I know).

Now, if you run the above code, even calling explicitly System.gc(), the objects are not collected while your function is on the stack: you can see they're still alive looking at the count of the dictionary (which is set to use weak references for obvious reasons).

When this count is traced again, in a different execution stack (caused by the asynchronous call to setTimeout), all objects have been freed.

So, I'd say it seems the GC is not the culprit of the poor performance in your case. Again, this is a mere observation and the fact that the GC didn't run while executing user code in this test doesn't mean it never will. Likely, it won't, but since this isn't documented, there's no way to know for sure, I'm afraid. I hope this helps.


GC will not run between two statements that are on the same stack / same frame like you have in your example. Memory will be freed prior to the execution of the next frame. This is how most modern VM environments work.


You can't determine when GC will run. If you are trying to stop something from being GC'd, keep a reference to it somewhere.


According to this Tom from Gabob, large enough orphaned hierarchies do not get garbage collected.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜