完成的,未引用的计时器的垃圾收集

时间:2015-03-14 00:54:20

标签: actionscript-3 timer garbage-collection

所以我有一个类似的定时器:

private var timer:Timer;

public function doThingLater():void {
    timer = new Timer(1000, 1);

    var someBigThing:SomeBigThing = new SomeBigThing();

    timer.addEventListener(TimerEvent.TIMER_COMPLETE, function():void {
        someBigThing.doThing();
        timer = null;
    });
    timer.start();
}

我需要确保someBigThing是垃圾收集的。

我的谷歌搜索结果一直告诉我,如果一个计时器永远不会停止,它将永远不会被收集,但那些停止的会怎么样?

而且我相当确定只将字段设置为null并不一定允许收集;如果我有这个:

public function doThingLater():void {
    //initialize timer...
    timer.start();
    timer = null;
}

我仍然希望计时器能够运行。

我知道可以确保通过在Timer上调用removeEventListener来收集它,但是监听器是匿名的;显然我可以做这个工作,但我认为匿名听众更具可读性。

1 个答案:

答案 0 :(得分:0)

是的,所以我做了一些实验,看看我能找到什么,我的代码如下。我不是100%肯定这一点,但我的主要发现点是:

  • 是的,停止计时器将最终被收集。
  • 删除侦听器的工作原理也一样。
  • 使用弱引用的侦听器也可以工作,只要对侦听器的另一个引用保持不变,直到不再需要它为止。

在实验中,我也得出了其他一些不太相关的结论:

  • 当垃圾收集工作难以预测时,即使用System.gc()强制执行
  • 也是如此
  • 匿名函数复制当前函数的整个激活记录,这导致另外两个令人不安的结论:
  • 匿名函数可能会保留对对象的意外引用,因此函数从

    返回
    function foo():Function {
        var thing:Object;
        //initialize thing
    
        //Do a bunch of other stuff.
    
        return function():void { /*do nothing*/ };
    }
    

    阻止thing被垃圾收集。

  • 匿名函数引用原始局部变量,所以

    var thing:SomeClass;
    
    var function:Function = function():void {
        thing.doAThing();
    };
    
    //do some stuff
    
    thing = null;
    
    如果使用function

    将导致空引用错误。

我会等几天,看看是否有人对计时器的收集方式有充分了解,然后接受我自己的答案。无论如何,这是测试代码:

package 
{
    import flash.display.Sprite;
    import flash.events.Event;
    import flash.events.KeyboardEvent;
    import flash.events.TimerEvent;
    import flash.system.System;

    import flash.utils.Dictionary;
    import flash.utils.Timer;

    public class Main extends Sprite 
    {
        public static var objects:Dictionary;

        private var control:Vector.<int>;

        private var doesNotStop:Timer;
        private var stops:Timer;
        private var removesListener:Timer;
        private var usesWeakListener:Timer;

        private static var before:Timer, after:Timer;

        private static var beforeTime:Number = 100;
        private static var time:Number = 1000;
        private static var afterTime:Number = 2000;
        private static var wellAfterTime:Number = 6000;

        public function Main():void 
        {
            if (stage) init(null);
            else
                addEventListener(Event.ADDED_TO_STAGE, init);
        }
        public function init(event:Event):void {
            doIt();

            printObjects("After releasing references, ");

            stage.addEventListener(KeyboardEvent.KEY_DOWN, test);
        }

        public function test(event:Event):void {
            printObjects("On key press, ");
        }
        public function doIt():void {

            objects = new Dictionary(true);

            //Create objects.
            var thing:Vector.<int> = createBigObject();

            control = createBigObject();

            var stopsObject:Object = initializeStops();

            var doesNotStopObject:Object = initializeDoesNotStop();

            var removesListenerObject:Object = initializeRemovesListener();

            var usesWeakListenerObject:Object = initializeUsesWeakListener();

            //Add them to the dictionary.
            objects[control] = "control";

            objects[thing] = "thing";

            objects[doesNotStop] = "does not stop";
            objects[doesNotStopObject] = "does not stop object";

            objects[stops] = "stops";
            objects[stopsObject] = "stops object";


            objects[removesListener] = "removes listener";
            objects[removesListenerObject] = "removes listener object";

            objects[usesWeakListener] = "uses weak listener";
            objects[usesWeakListenerObject] = "uses weak listener object";

            printObjects("Before releasing references, ");

            //Release local references.
            control = null;

            doesNotStop = null;
            doesNotStopObject = null;

            stops = null;
            stopsObject = null;

            removesListener = null;
            removesListenerObject = null;

            usesWeakListener = null;
            usesWeakListenerObject = null;


            //Start timers to check gc later.
            before = new Timer(beforeTime, 1);
            var listener:Function =  function():void {
                printObjects("Before the Timer has finished, ");
            };
            before.addEventListener(TimerEvent.TIMER_COMPLETE, listener, false, 0, false);
            before.start();

            after = new Timer(afterTime, 1);
            after.addEventListener(TimerEvent.TIMER_COMPLETE, function():void {
                printObjects("After the Timer has finished, ");
            });
            after.start();

            var wellAfter:Timer = new Timer(wellAfterTime, 1);
            wellAfter.addEventListener(TimerEvent.TIMER_COMPLETE, function():void {
                printObjects("Well after the Timer has finished, ");
            });
            wellAfter.start();
        }

        private function initializeDoesNotStop():Object {
            var object:Vector.<int> = createBigObject();

            doesNotStop = new Timer(time, 0);
            doesNotStop.addEventListener(TimerEvent.TIMER_COMPLETE, function():void {
                object[0] = 10;
            });
            doesNotStop.start();

            return object;
        }

        private function initializeStops():Object {
            var object:Vector.<int> = createBigObject();

            stops = new Timer(time, 1);
            stops.addEventListener(TimerEvent.TIMER_COMPLETE, function():void {
                object[0] = 10;
            });
            stops.start();

            return object;
        }

        private function initializeRemovesListener():Object {
            var object:Vector.<int> = createBigObject();

            var listener:Function = function(event:Event):void {
                object[0] = 10;
                (event.target as Timer).removeEventListener(TimerEvent.TIMER_COMPLETE, listener);
            };

            removesListener = new Timer(time, 1);
            removesListener.addEventListener(TimerEvent.TIMER_COMPLETE, listener);
            removesListener.start();

            return object;
        }

        private function initializeUsesWeakListener():Object {
            var object:Vector.<int> = createBigObject();

            usesWeakListener = new Timer(time, 1);
            usesWeakListener.addEventListener(TimerEvent.TIMER_COMPLETE, function():void {
                object[0] = 10;
            }, false, 0, true);
            usesWeakListener.start();

            return object;
        }

        public static function createBigObject():Vector.<int> {
            var out:Vector.<int> = new Vector.<int>(100, true);
            for (var i:int = 0; i < 100; i++)
                out[i] = i;

            return out;
        }

        public static function printObjects(msg:String):void {

            System.gc();

            var output:String = msg + "the following objects have not been collected: ";

            var objectNames:Vector.<String> = new Vector.<String>();

            for each (var value:String in objects) {
                objectNames.push(value);
            }

            objectNames.sort(0);

            for each (var str:String in objectNames) {
                output += str + ", ";
            }

            trace(output);
        }

    }

}

我粘贴了它,所以如果你自己测试就要小心格式化问题。 输出是:

在发布引用之前,未收集以下对象:控制,不停止,不停止对象,删除侦听器,删除侦听器对象,停止,停止对象,事物,使用弱侦听器,使用弱侦听器对象,< / p>

释放引用后,未收集以下对象:控制,不停止,不停止对象,删除侦听器,删除侦听器对象,停止,停止对象,事物,使用弱侦听器,使用弱侦听器对象,< / p>

在Timer完成之前,尚未收集以下对象:不停止,不停止对象,删除侦听器,删除侦听器对象,停止,停止对象,事物,使用弱侦听器,

Timer完成后,未收集以下对象:不停止,不停止对象,删除侦听器,删除侦听器对象,停止,停止对象,事物,使用弱侦听器,

在Timer完成后,以下对象尚未收集:不停止,不停止对象,事物,

按键时,未收集以下对象:不停止,不停止对象,事物,