先了解下回收的原理,如果觉得麻烦,可以直接看最后的方法
摘自:http://www.duzengqiang.com/blog/article.asp?id=245
AS3垃圾收集器不再支持自动为你收集垃圾等假设。也就是说Flash开发者转到ActionScript 3.0之后需要对关于垃圾收集如何工作以及如何编程使其工作更加有效具备较深入的理解。没有这方面的知识,即使创建一个看起来很简单的游戏或应用程序也会 出现SWF文件内存泄露、耗光所有系统资源(CPU/内存)导致系统挂起甚至机器重启。
要理解如何优化你的ActionScript 3.0代码,你首先要理解垃圾收集器如何在Flash Player 9中工作。Flash有两种方法来查找非活动对象并移除它们。本文解释这两种技术并描述它们如何影响你的代码。
本文结尾你会找到一个运行在Flash Player9中的垃圾收集器模拟程序,它生动演示了这里解释过的概念。
关于垃圾收集器
垃圾收集器是一个后台进程它负责回收程序中不再使用的对象占用的内存。非活动对象就是不再有任何其他活动对象引用它。为便于理解这个概念,有一点非常重 要,就是要意识到除了非原生类型(Boolean, String, Number, uint, int除外),你总是通过一个句柄访问对象,而非对象本身。当你删除一个变量其实就是删除一个引用,而非对象本身。
以下代码很容易说明这一点:
ActionScript 代码
// create a new object, and put a reference to it in a:
var a:Object = {foo:"bar"}
// copy the reference to the object into b:
var b:Object = a;
// delete the reference to the object in a:
delete(a);
// check to see that the object is still referenced by b:
trace(b.foo); // traces "bar", so the object still exists.
如果我改变上述示例代码将b也删除,它会使我创建的对象不再有活动引用并等待对垃圾收集器回收。ActionScript 3.0 垃圾回收器使用两种方法定位无引用的对象 : 引用计数法和标识清除法。
引用计数法
引用计数法是一种用于跟踪活动对象的较为简单的方法,它从 ActionScript 1.0开始使用。当你创建一个指向某个对象的引用,该对象的引用计数器加1;当你删除该对象的一个引用,该计数器减1。当某对象的计数器变成0,该对象将被标记以便垃圾回收器回收。
这是一个例子:
ActionScript 代码
var a:Object = {foo:"bar"}
// the object now has a reference count of 1 (a)
var b:Object = a;
// now it has a reference count of 2 (a & b)
delete(a);
// back to 1 (b)
delete(b);
// the reference count down is now 0
// the object can now be deallocated by the garbage collector
引 用计数法简单,它不会非CPU带来巨大的负担;多数情况下它工作正常。不幸地是,采用引用计数法的垃圾回收器在遇到循环引用时效率不高。循环引用是指对象 交叉引用(直接、或通过其他对象间接实现)的情况。即使应用程序不再引用该对象,它的引用计数器仍然大于0,因此垃圾收集器永远无法收集它们。下面的代码 演示循环引用是怎么回事:
ActionScript 代码
var a:Object = {}
// create a second object, and reference the first object:
var b:Object = {foo:a};
// make the first object reference the second as well:
a.foo = b;
// delete both active application references:
delete(a);
delete(b);
上 述代码中,所有应用程序中活动的引用都被删除。我没有任何办法在程序中再访问这两个对象了,但这两个对象的引用计数器都是1,因为它们相互引用。循环引用 还可以更加负责 (a 引用 c, c引用b, b引用a, 等等) 并且难于用代码处理。Flash Player 6 和 7的XML对象有很多循环引用问题: 每个 XML 节点被它的孩子和父亲引用,因此它们从不被回收。幸运的是Flash Player 8 增加了一个叫做标识-清除的新垃圾回收技术。
标识-清除法
ActionScript 3.0 (以及 Flash Player 8) 垃圾回收器采用第2种策略标识-清除法查找非活动对象。Flash Player从你的应用程序根对象开始(ActionScript 3.0中简称为root)直到程序中的每一个引用,都为引用的对象做标记。
接下来,Flash Player遍历所有标记过的对象。它将按照该特性递归整个对象树。并将从一个活动对象开始能到达的一切都标记。该过程结束后,Flash Player可以安全的假设:所有内存中没有被标记的对象不再有任何活动引用,因此可以被安全的删除。图1 演示了它如何工作:绿色引用(箭头)曾被 Flash Player 标记过程中经过,绿色对象被标记过,白色对象将被回收。
Figure 1. Flash Player采用标记清除方法标记不再有活动引用的对象
标记-清除法非常准确。但是,由于 Flash Player 遍历你的整个对象结构,该过程对CPU占用太多。Flash Player 9 通过调整迭代标识-清除缩减对CPU的占用。该过程跨越几个阶段不再是一次完成,变成偶尔运行。
延期(执行)垃圾回收器和不确定性
Flash Player 9垃圾回收器操作是延期的。这是一个要理解的非常重要的概念:当你的对象的所有引用删除后,它不会被立即删除。而是,它们将在未来一个不确定的时刻被 删除(从开发者的角度来看)。垃圾收集器采用一系列启发式技巧诸如查看RAM分配和内存栈空间大小以及其他方法来决定何时运行。作为开发者,你必须接受这 样的事实:不可能知道非活动对象何时被回收。你还必须知道非活动对象将继续存在直到垃圾收集器回收它们。所以你的代码会继续运行(enterFrame 事件会继续)、声音会继续播放、装载还会发生、其它事件还会触发等等。
下面介绍回收的有效方法:
由这副图可以看出来 当你的类中包含的对象越多,嵌套越深,对象越复杂,系统回收发费时间就越长。而我们如果自行把这些对象与嵌套拆开来,使得每个复杂的对象变成一个一个的非常小的对象,那么回收所花的时间就会大大减少了。所以我们写完类,记得给类加上一个垃圾回收器,如:
package buildworld
{
import flash.display.Sprite;
import mypkg.MapBlock;
public class Map extends Sprite
{
private var map:Array = new Array();
private var mapName:String = "map1";
public function Map(nw:int,nh:int):void
{
this.createMap();
}
public function createMap():void
{
for(var h = 0;h < this.nHeight;h++)
{
this.map[h] = new Array();
for(var l = 0;l < this.nWidth;l++)
{
//MapBlock是个类,
this.map[h][l] = new MapBlock();
}
}
}
//垃圾回收器
public function gc():void
{
for(var h = 0;h < this.nHeight;h++)
{
for(var l = 0;l < this.nWidth;l++)
{
this.removeChild(this.map[h][l]);
this.map[h][l].gc();//调用MapBlock中的垃圾回收器
this.map[h][l] = null;
}
}
mapName = null;
}
}
}
那么最终进行垃圾回收就方便多了。
不知道大家还有更好的方法没?