views:

125

answers:

5

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 only 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

A: 

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.

ThatSteveGuy
hi, thanks for the reply.i know that i can't determine in the big scheme of things when the GC runs, but my question is if i can know that it won't run in the middle of executing say "x += 1".
orion elenzil
Do you have Flex Builder? It has a memory profiler that may answer some o your questions.
ThatSteveGuy
+1  A: 

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.

Juan Pablo Califano
awesome, thank you for the investigation. i think you're right.
orion elenzil
also fwiw, i've heard and experimentally verified that System.gc() merely requests the system to do a sweep, and in my tests it seemed to generally be ignored.
orion elenzil
@orion elenzil. In my experience, the gc almost always runs (wether I call it from code or from FlexBuilder profiler). There are some caveats, though. You must be playing the swf with the debug version of the player and sometimes, it seems you need to call it twice to make it run (go figure!). Also, it most likely runs with a delay anyway (probably after user code has executed).
Juan Pablo Califano
@juan - interesting. i may have been mis-interpreting what i was seeing. i was indeed using a recent debug player, and calling GC twice.
orion elenzil
+1  A: 

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.

jojaba
Are you trying to say that most modern VMs only run GC between function calls? Where did you get this?
Gabe
No. Actually I am saying that a gc in a modern vm is not going to expend processing to execute unneeded freeing between statements. Flash, iPhone, java, and .net are modern in my opinion modern. They wait until the thread sleeps, stacks complete, or runloops finishes.
jojaba
iPhone doesn't have a GC!
TandemAdam
@TandemAdam Of course. I'm referring to the memory pool, not GC.
jojaba
+3  A: 

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.

Gunslinger47
sweet, thank you.
orion elenzil
@Gunslinger47. +1. That's a better test than the one I came up with. Hadn't thought of brute forcing `new`!
Juan Pablo Califano
+5  A: 

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.

fenomas
Hehe, If I had clicked the questioner's link before answering I'd have seen that a lot of this was covered. Well, hopefully the last two paragraphs are still helpful..
fenomas
excellent, thank you for the thoughtful response.all in all, it sounds like it's very unlikely that the GC is part of the sporadic performance issue i'm seeing, which is a very good thing ! i can fix my code, but i can't fix the GC. many thanks for the replies.
orion elenzil