AS3中Timer,setTimeout,setInterval对gc的对比

来源:互联网 发布:通化教务网络管理系统 编辑:程序博客网 时间:2024/06/15 09:47

Timer

1. 官方说法

  无限次Timer可能会引起内存泄露,有限次Timer不会引起内存泄露

2. 实际情况(见测试用例)

  a)无限次Timer

    i.Timer在运行中且注册了TimerEvent.Timer事件侦听器,则侦听器对象本身不会被回收

    ii.Timer没有调用start()或者start后调用了stop(),侦听器对象会被垃圾回收

    iii.Timer调用了start(),但是没有对Timer注册事件侦听器或者事件侦听器被移除,不存在对侦听器垃圾回收问题

  b)有限次Timer

    i.Timer在运行中且对Timer注册了事件侦听器,则侦听器对象不会被回收

    ii.Timer调用了stop(),侦听器对象会被回收

    iii.Timer在运行了指定的repeat次数后,侦听器对象会被回收

3.结论

  Timer在运行中且注册了事件侦听器(一般用Timer,肯定会注册事件侦听器),侦听器对象不会被回收;

  由于有限次Timer迟早有结束的那一刻,所以有限次Timer一般不会造成内存泄露,但是可能会造成侦听器对象延迟释放;

  而无限次Timer则可能引起内存泄露,在一个对象释放后,如果对象内部的Timer没有停止或者没有移除事件侦听器,则对象永远不会被释放

4.好的习惯

  a)能用有限次Timer的情况,不要用无限次Timer

  b)无论是有限次Timer还是无限次Timer,在不再需要Timer时显式地调用Timer.stop(),或者移动事件侦听器(removeEventListener),并且建议两项工作都做。

5.测试用例及说明

  测试一中,在文档类中创建了一个TestTimer(test),注意这是一个局部变量。在TestTimer构造函数中,创建了一个每100毫秒触发一次的无限次Timer,并添加相应的侦听器,最后调用Timer.start()启动Timer,这时虽然程序中没有任何对TestTimer的引用,运行GC可以发现TestTimer对象不会被销毁;

  在测试二中,与测试一唯一不同的地方在于没有对timer添加事件侦听器,即少了timer.addEventListener(TimerEvent.TIMER, handleTimer),在profile中运行gc,发现testTimer的实例由1变为0,说明这种情况不会造成内存泄露;

  在测试三中,与测试二的不同在于,添加了事件侦听器,但是没有调用timer.start(),这时发现结果与测试二相同;针对测试三,可以测试另一种情况,在构造函数中调用timer.start(),但是在handleTimer中调用timer.stop()或者removeEventListner(TimerEvent.TIMER, handleTimer),测试结果与测试二相同;

  在测试四中,指定了一个执行200次的Timer(new Timer(100,200)),在Timer执行结束前,无法回收TestTimer实例,在执行完毕后可以正常回收,说明侦听器是否能被gc,是完全依赖于Timer是否在运行中,对于无限次Timer,如果不调用stop(),侦听器则不能被回收;

public function Test(){           var test:TestTimer = new TestTimer();       }public class TestTimer {       private var timer:Timer;       public function TestTimer() {           timer = new Timer(100);           timer.addEventListener(TimerEvent.TIMER, handleTimer);           timer.start();       }       private function handleTimer (event:TimerEvent):void {           trace(“timer”);       }}

setInterval

     如果不调用clearInterval,setInterval调用的函数所在对象永远不会被回收,这是显而易见的,有怀疑精神的童鞋可以自己去做测试,就不写测试代码了;如果怀疑这个的话,建议也去测试一下,as中(1+1)==2是不是返回true,o(^_^)o~~~哈哈!!

setTimeout

  1, 如果不调用clearTimeout,setTimeout引用的对象永远不会被回收,无论timeoutHandler是否已经执行,clearTimeout的作用不仅仅是中断未执行的timeoutHandler,更重要的是,如果不调用clearTimeout,声明timeoutHandler的对象不会被gc

  2, 测试如下代码,发现gc时,TestTimeOut的实例能够正常释放;

  3, 如果在下面的代码块的timeoutHandler中不调用clearTimeout(timeoutId),虽然此时timeoutHandler已经执行过,TestTimeOut对象在gc执行时仍然不会被回收;

  4, 关于setTimeout,多说一句, setTimeout其功能相当于只执行一次的Timer,而Timer执行一次后,无论是否stop()或者removeEventListener,其事件侦听对象都可以被正常gc;个人觉得这应该也算是设计上的失败,我一直以为clearTimeout只是用来在timeoutHandler执行之前用来取消setTimeout的;

  而且其中文帮助文档是这样说的(如果是翻译出错,算我倒霉):

  如果打算使用 clearTimeout() 方法取消 setTimeout() 调用,请确保将 setTimeout() 调用分配给一个变量(clearTimeout() 函数稍后将引用该变量)。如果不通过调用 clearTimeout() 函数取消 setTimeout() 调用,则不会将包含设置的超时 closure 函数的对象作为垃圾回收。

  如果只看前半句,我绝对不会想到这个还会影响到gc,小小地钻一下文字牛角尖:如果我不打算取消本次setTimeout()调用,那么是不是也就不必要记住tiemeoutId,那么也就无从调用clearTimeout了;

  后半句,个人认为是有歧义的(当然还是可能是翻译出了差错),而且我想了很久,这段话连起来读,第二种理解也符合一些:

  理解一:如果不调用clearTimeout,那么包含超时closure函数的对象永远也无法垃圾回收(无论超时回调closure函数是否已经执行),这个和测试结论一致,也是正确的理解;

  理解二:如果不调用clearTimeout函数,在设置的超时closure函数在执行前,包含该函数的对象不能作为垃圾回收;

  假设设置的超时closure函数所在对象没有其他引用,或者closure只是在主函数内部定义的一个局部函数, setTimeout调用执行前的确是不应该回收的,这样第二种理解也可以自圆其说。

public function Test(){           var test:TestTimeOut = new TestTimeOut ();    }public class TestTimeOut {       private var timeoutId:int;       public function TestTimeOut () {           timeoutId = setTimeout(timeOutHandler);       }       private function timeOutHandler ():void {           clearTimeout(timeoutId);       }}

转自:http://blog.163.com/fll_0601/blog/static/5499988201111843136807/