内存优化在项目是一个重要的环节,如果不合理的利用和回收内存会合你的程序整体大大下降.
合理使用对象
创建不同对象一般所消耗的内存是不一样的。如:Number 消耗 8 个字节,int消耗 4个字节, uint消耗 4个字节.下面我举一些例子:
1. int 类可使用表示为 32 位带符号整数的数据类型。 int 类表示的值的范围是:-2,147,483,648 (-2^31) 到 2,147,483,647 (2^31-1),所以如果你的取值范围在-2,147,483,648 (-2^31) 到 2,147,483,647 (2^31-1)请你用int而不是用Number(刚从2.0转过来的人可能喜欢用Number).uint 范围是0 到 4,294,967,295 (2^32-1) 之间,Actionscript3中类型很少,所以这些只要你平时稍加注意一下就行.
2. 合理使用Shape与Sprite,MovieClip,你可能用MovieClip可以完成Sprite与Shape的功能,但是他们所需的内存是不一样的Shape需要 236 字节,Sprite 需要 412字节, MovieClip 需要440字节,如果你只想显示图形没有交互那你使用Shape,如果是有交互的图形你可以用Sprite,如果是动画你才用MovieClip.
以上只是2个常见的实例,其实在as3中还有很多值得注意的这类情况。我还看到有些大哥为了派发一个事件而去继承Sprite类,Sprite需要消耗400字节, EventDispatcher只需要40字节.
对象重用
简单的说就是重复使用对象,而不是释放再重新申请,你可能这么认为:释放一个对象回收100字节的空间,我重新在new一个又占用100字节,返正都得占用100字。在flashplayer中不是说你想释放就能释放的,垃圾回收是由flashplayer来执行的,程序是不能控制,更不知道他是什么时候来执行的.所以对于我们来说回来是完全不可控的.所以说你觉得可回收了对象,很可能没回收继续占用空间直到flashplayer觉得内存不够时才可能执行垃圾回收从而被释放.而执行垃圾回收是非常耗费资源的操作尤其在大型的项目中.
虽然我们不能控制垃圾回收,但是我们可以降低垃圾回收器执行的次数,这就是我们尽量做到不去new,而使用现有的对象。这些做还有个好处就是,节省了创建对象的性能开销.
这里是不断的创建对象
var size:Rectangle;
var bitmap:BitmapData=new BitmapData(100,100);
for (var:int = 0; i < 100; i++)
{
size = new Rectangle(i,0,1,10);
myBitmapData.fillRect(size,COLOR);
}
重用对象
var bitmap:BitmapData=new BitmapData(100,100);
var size:Rectangle = new Rectangle(0,0,1,10);
for (var:int = 0; i < 100; i++)
{
size.x = i;
myBitmapData.fillRect(size,COLOR);
}
对象池技术
对象池技术的原理就是回收不使用的对象而不是遗弃等待FlashPlayer垃圾回器的执行,等到再需要时再拿来使用.这种技术使用非常广泛,看下面的简单的实现(这里只是一个抛砖引玉,具体怎么设计这个对象池,要看各位的具体项目了).
IRecyclable接口,可以被回收对象必须实现。再就是ObjectPool.
对象的另类存储
在存储大量对象时,我们可以以另一种方式存储或者叫序列化,即存储对象的数据而不存储具体的对象,当需要时再根椐需要数据返序列化出一个对象。这样做法的好处在于不会有大量的对象产生,在as中对即使是空对象也会占用40个字节.
很多时候你可能这样写代码:
var p:Array=[];
for(var i:int=0;i<1000000;i++){
p.push(new Point(i,i))
}
这里我来实现另一种对象存储:
package{
import flash.geom.Point;
public class PointContainer{
private var container:Array
public function PointContainer()
{
container=[];
}
public function add(v:Point):void{
container.push(v.x);
container.push(v.y);
}
public function getPointAt(index:int):Point{
return new Point(container[index],container[index+1]);
}
}
}
//使用
var p:PointContainer=new PointContainer
for(var i:int=0;i<1000000;i++){
p.add(new Point(i,i))
}
for(var i:int=0;i<1000000;i++){
p.getPointAt(i);
}
这种对象存储方式适合大量小型对象存储,比如粒子系统.如果是庞大的对象,这种方式没有任何优式.
事件优化
使用事件模型通信与使用传统的回调函数相比,速度更慢且占用的内存更多
AS3中事件的派发传递参数是采用Event,所以在高频率派发事件的地方你可以采用传统的回调函数这样可以大大提高你的效率和内存消耗
位图优化
一般最占用内存的部分就是位图,在我开发的MMO游戏中90%以上的的内存是由位图占据的,所以在位图的使用过程序要特别注意,不使用的位图一定要释放掉。在这里我提一些小的建议,以尽量控制位图的内存占用。
1. 能共享位图的尽量共享,具体做法就是用一个BitampData创建多个Bitamp对象。尽量不要去复制BitmapData对象.
2. 将滤镜应用于显示对象时,Flash Player 将在内存中创建两个位图,所以这需要大量内存。所以尽量不要去使用滤镜,一般可以用ps做好滤镜后生成位图给flash来使用.
3. 合理的使用位图缓存.对矢量图形做位图缓存,其实在把矢量图形变成位图,并使用该
位图进行呈现,此会显著提高呈现的性能,但需要占用大量内存。针对复杂的矢量内容使用位图缓存功能。
释放对象
释放对象其实只有一句话,就是不支有对象的引用,包括声音/视频流,socket,件事等等.我最多的一种情况是事件忘记移除导致对象无法回收,这并不是我不知道这一点,而是在写代码时的疏忽。如果你是一个人开发,你可能经常去profile你的代码,可能很容易找出哪个地方没被移除,但是如果你主程或者架构师你手下有很多少人在Coding,你怎么让他不遗忘移除事件呢,下面我来简单介绍一种方法一次性移除所有事件,避免一个一个移除带来的遗漏问题.
一般大家都用EventDispatcher来派发事件.现在我们就对addEventListener进行一个小小的改造即可.
package
{
import flash.events.EventDispatcher;
import flash.events.IEventDispatcher;
public class MyEventDispatcher extends EventDispatcher{
private var events:Array
public function MyEventDispatcher(target:IEventDispatcher=null)
{
super(target);
events=[];
}
override public function addEventListener(type:String, listener:Function, useCapture:Boolean=false, priority:int=0, useWeakReference:Boolean=false):void{
events.push({type:type,fun:listener})
super.addEventListener(type, listener, useCapture, priority, useWeakReference);
}
override public function removeEventListener (type:String, listener:Function, useCapture:Boolean = false):void{
super. removeEventListener(type,listener, useCapture);
for(var i:int=0;i< ;i++){
if( events[i].type==type && events[i].fun==listenner){
events.splice(i,1)
}
}
}
public function dispose():void{
var ev:Object;
while(events.length){
ev=events.pop();
super.removeEventListener(ev.type,ev.fun);
}
}
}
}
所以你在使用EventDispatcher的地方全部使用MyEventDispatcher即可,在回收之前端调用一下dispose方法,就会内部移除所有事件.
还有很多方法可以做到这一点,以上方法只是一个抛砖引玉.
滥用强制垃圾回收
在Flashplayer debug版本提供了System.gc()接口,可以让虚拟机执行垃圾回收,但是flashplayer 普通用户版是没有这个接口的,于是有人想出使用异常还出发垃圾回收如:
function gc():void{
try{
(new LocalConnection).connect("foo");
(new LocalConnection).connect("foo");
}catch(e){
trace(System.totalMemory);
}
}
当然还有其他方法在这里我就不多举了.
Adobe为什么在普通用户版的flashplayer中取消对System.gc()接口的支持,adobe肯定不希望用户直接去触发垃圾回器,肯定是有理由的,下面我将对这个理由进行的浅薄分析.
垃圾回收器通过查找系统中的相互引用,从而检测出处于非活动状态的对象。将删除通过这种方式检测到的处于非活动状态的对象。也就说他要扫描可能持有对象的的所有变量,这一点所需的代价很大,尤其是在大型项目中.如果你经常去做这样的事,这会大大浪费你的CPU资源.
我做Flash开发已快5年了,我没有什么地方非要用到强制垃圾回收的地方.所以我要在这里提醒广大Flash开发者,在没有特术需求的时候不要使用强制垃圾回收,不要会了腾出一点内存空间而去强制垃圾回收,可能很多时候你是检芝麻丢西瓜,回收器的执行不是普通程序员管的事,Flashplayer会选择最合适的时候去调用.(原本这一节不是我要写的内容,原因是我看到在天地会主页很醒目的位置有一篇《AS3强制内存回收方法之二》,而且受到大家的广泛关注,所以我不想让这篇文章误导了一些新手,以上只是个人见解我希望大家一起讨论这个问题).