垃圾回收:一个跟踪过程,它传递性地跟踪指向当前使用的对象的所有指针,以便找到可以引用的所有对象,然后重新使用在此跟踪过程中未找到的任何堆内存。垃圾回收可以有效的防止内存泄露,有效的使用可以使用的内存。垃圾回收器通常是作为一个单独的低级别的线程运行,不可预知的情况下对内存堆中已经死亡的或者长时间没有使用的对象进行清除和回收。
ActionScript3采用的是AVM2(据官方说代码执行速度比AVM1最多可快10倍),但其仍是一个低频率的垃圾回收机制,即只有在申请内存时才会触发GC自动回收内存,
垃圾回收条件:没有引用
很多人都多少在书本或者网贴上看到过有关垃圾回收机制的介绍,但是鲜有人付诸于实践,在写代码的时候常常没有这种意识,所以很多人会在QQ群里或者论坛上 求助说“哎~我的应用怎么那么卡?求优化方案”之类的话。事实上,在Flash开发过程中,让画面流畅(保持高帧率且不降帧)的关键是节省CPU和内存的 消耗。节省CPU消耗的关键在于算法以及使用的对象类型上的选择,而节省内存消耗的关键则在于编码方式上面,这也是有经验的程序员和菜鸟之间的差距之一。
引用计数法与标记清除法
在AVM2中,除去特殊的BitmapData必须调用dispose才能回收内存外,其他的部分都是用引用计数法和标记清除法作为判断是否应该回收内存的手段,而且并没有提供主动回收的API。
引用计数法:引用计数法是一种用于跟踪活动对象的较为简单的方法,它从ActionScript1.0开始使用。当你创建一个指向某个对象的引用,该对象的引用计数器 加1;当你删除该对象的一个引用,该计数器减1。当某对象的计数器变成0,该对象将被标记以便垃圾回收器回收。
优点:引用计数法简单,它不会给CPU带来巨大的负担;多数情况下它工作正常。缺点:采用引用计数法的垃圾回收器在遇到循环引用时效率不高。ps:循环引用是指对象 交叉引用(直接、或通过其他对象间接实现)的情况(a 引用 c, c引用b, b引用a, 等等)。即使应用程序不再引用该对象,它的引用计数器仍然大于0,因此垃圾收集器永远无法收集它们。
标记清除法:FlashPlayer从你的应用程序根对象开始(ActionScript3.0中简称为 root)直到程序中的每一个引用,都为引用的对象做标记。 接下来,FlashPlayer遍历所有标记过的对象。它将按照该特性递归整个对象树。并将从一个活动对象开始能到达的一切都标记。该过程结束 后,FlashPlayer可以安全的假设:所有内存中没有被标记的对象不再有任何活动引用,因此可以被安全的删除。
优点:标记-清除法非常准确,避免了循环交叉引用情况。缺点:由于FlashPlayer 遍历你的整个对象结构,该过程对CPU占用太多。ps:FlashPlayer 8增加了标记清除法垃圾回收机制,FlashPlayer 9 通过调整迭代标识-清除缩减对CPU的占用。该过程跨越几个阶段不再是一次完成,变成偶尔运行。
弱引用
在AS3众多的新特性中,我灰常高兴的看到“weak references”。这种引用不会被垃圾收集器作为判定object是否被回收的依据。它的作用是:如果当一个对象仅仅剩下弱引用时,这个对象将会被垃圾收集器在下一轮回收。但是弱引用只支持两种类型:第一种是经常会因为内存管理机制带来麻烦的事件监听器,我强烈的建议:每当添加监听器时,都将其第五个参数选项,设置为true即弱引用(监听可以被垃圾回收,ps:有注册监听必须要有移除监听)。另一种弱引用支持的是Dictionary object。一般情况下在初始化时设置其第一个参数为true,
垃圾回收常用的方法:
System.disposeXML()
BitmapData.dispose()
Loader.unloadAndStop()
removeChild()
removeEventListener()
设置引用为null
查看内存方法:System.totalMemory
一种可以强制执行垃圾回收的方法(不推荐使用)
在AS3中垃圾回收周期是不确定的,没有方法可以知道它下一次什么时候运行。严格的讲这句话也不完全的对,有一个技巧可以强制让flash播放器执行一次垃圾回收,这个技巧很方便你去探索垃圾回收和在开发期内测试你的程序,但是它绝不能出现在开发完成的产品中,因为它会破坏处理器的负载能力。同时官方也是不推荐使用的,所以你不能靠它的功能来完成实质功能上提升。
强制执行垃圾回收,你所要做的就是 执行两次相同的LocalConnection。这样做系统会抛出一个异常,所以你必须为它准备好异常捕捉(try/catch)
try { import flash.net.LocalConnection; var conn1:LocalConnection = new LocalConnection (); var conn2:LocalConnection = new LocalConnection (); conn1.connect("gc"); conn2.connect("gc"); }catch(e:Error){}
再次重复一次:这个方法仅仅可以用于开发周期内的测试。它绝不能出现在开发完成的产品中!
今天先写到这儿吧,如果以后有时间整理我会加上一些代码实例,那样就更直观了。【第一篇总结性博客,忘与大家共同学习欢迎拍砖。】