开发者

ActionScript - Forced Garbage Collection Not Working In ADL?

开发者 https://www.devze.com 2023-01-25 05:15 出处:网络
when launching the following code in ADL, why does the square continue to rotate? var square:Sprite = new Sprite();

when launching the following code in ADL, why does the square continue to rotate?

var square:Sprite = new Sprite();
square.graphics.beginFill(0xFF0000);
square.graphics.drawRect(-25, -25, 50, 50);
square.x = square.y = 100;
addChild(square);

addEventListener(Event.ENTER_FRAME, rotateSquare, false, 0, true);

function rotateSquare(evt:Event):void
    {
    square.rotation += 2;
    }

System.gc();

Update

the following display object has a weak referenced ENTER_FRAME event listener. however, calling:

removeChild(testInstance);
testInstance = null;

doesn't stop the ENTER_FRAME event:

package
{
import flash.display.Sprite;
import flash.events.Event;

public class Test extends Sprite
    {       
    private var square:Sprite;

    public function Test()
        {
        addEventListener(Event.ADDED_TO_STAGE, init);
        }

    private function init(evt:Event):void
        {
        removeEventListener(Event.ADDED_TO_STAGE, init);

        square = new Sprite();
        square.graphics.beginFill(0xFF0000);
        square.graphics.drawRect(-25, -25, 50, 50);
        square.x = square.y = 100;
        addChild(square);

        addEventListener(Event.ENTER_FRAME, rotateSquare, false, 0, true);

//      //Current Solution - only works on display objects
//      addEventListener(Event.REMOVED_FROM_STAGE, removeHandler);
        }

    private function rotateSquare(evt:Event):void
        {
        trace("square is rotating");
        square.rotation += 2;
        }

//  private function removeHandler(evt:Event):void
//      {
//      removeEventListener(Event.REMOVED_FROM_STAGE, removeHandler);
//      removeEventListener(Event.ENTER_FRAME, rotateSquare);
//      }
    }
}

i have added a REMOVED_FROM_STAGE event listener, but this will only wor开发者_如何学编程k on display objects.

is this problem specific to ENTER_FRAME event?


Regarding your update, I think you are misunderstanding how GC works. The basic idea is rather simple.

When you create an object, flash allocates some memory in a storage called the heap. A reference to this object is returned. This reference is what you store in a variable. What is a reference? A means to access this object. Think of it as link to the object.

var foo:Bar = new Bar();

Now, in some languages, at some point you have to release the memory allocated for this object when you're done with it, or you have a memory leak.

In a GC environment, this is done automatically. Of course, you need some rules. This rules vary depending on the concrete GC, but in general terms, you could say the GC determines that an object is collectable if it's no longer reachable. This makes sense, because if you can't reach an object, you can't use it. You've lost your link to it. So, it's considered garbage and will be eventually collected.

The specifics on how reachability is determined vary, but in flash it's a mix of reference counting and a mark and sweep algorithm.

(The following is just a high level overview, the details might not be exact)

One method is reference counting: it's easy and fast but it doesn't work in all situations. Basically, each object has a reference count. Each time you assign this object to a variable (i.e. you store a reference to the object), the reference count is incremented. Each time you lost this reference (for instance, you null out your var), this count is decremented. If the count reaches 0, it means the object is unreachable and so it's collectable.

This works fine in some cases, but no others. Specially when there are crossed references.

var foo1:Bar = new Bar();   //  let's call this object Bar_1
var foo2:Bar = new Bar();   //  let's call this one Bar_2
//  at this point, Bar_1 has reference count of 1 (foo1) and Bar_2 has a reference of 1 (foo2)
foo1.theOtherFoo = foo2;
//  now Bar_2 has a RC of 2: foo2 and foo1.theOtherFoo
foo2.theOtherFoo = foo1;
//  now Bar_1 has a RC of 2: foo1 and foo2.theOtherFoo
foo1 = null;
//  foo1 no longer references Bar_1, so its RC is decremented. 
foo2 = null;
//  foo2 no longer references Bar_2, so its RC is decremented. 
//  but still both Bar_1 and Bar_2 have a RC of 1. 

As you can see, both Bar_1 and Bar_2 have a RC of 1, but are unreachable. This is one of the cases where reference counting doesn't work. Because for all intents and purposes, both objects are unreachable and yet won't be collected.

That's why there's a mark/sweep algorithm. From a high level point of view, what it does is traversing your objects graph, starting from some root objects and analize its relationships to determine whether an object is reachable or not. This algorithm will determine that even though Bar_1 and Bar_2 have a RC of 1, they're not reachable and thus should be considered garbage and be collected at some point.

Events, listeners and dispatchers work the same way. They're not a special case. When you do:

function test():void {
    foo1.addEventListener("someEvent",someHandler);
}

function someHandler(e:Event):void {
}

It's the same as doing:

function test():void {
    foo1.someProperty = this;
}

The effect is that foo1 now has a reference to this. You'd normally call removeEventListener when you're done for 2 reasons:

1) You no longer want foo1 to have a reference to this. 2) You no longer want to listener for "someEvent" events.

Lots of people insist on using weak references, because they think that then you can pretend you don't have to call removeEventListener (which is apparently too hard...). This is wrong. removeEventListener does two things and both are important. If you want to stop receiving notifications for some event, you have to tell the dispatcher. It's really that simple. In my opinion, weak references are innecesary in most cases. Some advocate to use them by default; but in my experience, in practice this is a bad service to them, as it confuses people further, encourages them to write sloppy code and gives them the impression that you can ignore how this very basic feature of the language (which is not that hard to graps) works.

Now, after this rather long (but hopefuly constructive) rant, let's look at your code:

Your sprite is not going to be collected, because it has 2 references:

1) the square variable 2) the stage.

The first follows the rules outline above. The second too, but it might not be so obvious at first sight. But it makes sense if you think about it for a second. When you did this:

addChild(square);

square got added to the Test instance, which is in turn added to the stage. The stage is always alive, so if something can be reached from the stage, it's reachable. As long as square remains added to the stage, you can be sure it won't be collected.

So, if you at some point do what Sean Thayne suggested:

removeChild(square)
square = null;

your Sprite will be collectable. That doesn't affect the fact that you told your Test object that you wanted to be called whenever a frame is entered. And that's exactly what's happening. Until you don't tell it you don't want to receive this event anymore (calling removeEventListener), it will call you back.


Flash's garbage collection only clears out elements/objects/variables that have either a zero reference count or have only weak references.

This means you would need to do this. For it to truly be gc'd.

removeChild(square)
square = null;
System.gc()
0

精彩评论

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