博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
AS3游戏中可视对象上限及位图相关的内存消耗实测
阅读量:4106 次
发布时间:2019-05-25

本文共 25161 字,大约阅读时间需要 83 分钟。

原文地址:

前些天连续做了一些测试,以加深对AS3的掌握和在项目中对游戏性能、效率优化方面的一些处理,有很多测试实际意义不大,都不过是证明一些猜想是正确的,除此没有什么。

但前天进行的一系列测试中,有一些对游戏开发中的内存占用,CPU占用方面有些意义,我打算逐渐写几篇讨论性的文章与大家共享。
由于最近在做的是2D的等距视角游戏,全部采用的位图处理,与3D无关,所以关注的也是这方面的问题,考虑问题的出发点也是这些方面,因此关注面还是比较狭窄的。

一、先从这类2D游戏中常用的对象类型的简单测试来开始看AS3的底层是如何支持

这只能是管窥一斑,我也只能是据此做点猜测,至于AS3底层到底如何实现,大可不必去搞的很清楚,但测试的结果却可以在我们写代码时,作到心中有数。
先来看测试函数代码

[javascript]
  1. private function compareDisplayObject():void  
  2. {  
  3.         len1 = System.totalMemory;  
  4.         _txt.text = "测试前总内存大小为:" + len1.toString();  
  5.           
  6.         var sprite:Sprite = new Sprite();  
  7.         len2 = System.totalMemory;  
  8.         _txt.appendText("\nnew Sprite后总内存大小为:" + len2.toString());  
  9.         _txt.appendText("\nnew Sprite后增加的内存为:" + (len2 - len1).toString());  
  10.           
  11.         var bmp:Bitmap = new Bitmap();  
  12.         len1 = System.totalMemory;  
  13.         _txt.appendText("\n\nnew Bitmap后总内存大小为:" + len1.toString());  
  14.         _txt.appendText("\nnew Bitmap后增加的内存为:" + (len1 - len2).toString());  
  15.           
  16.         var shape:Shape = new Shape();  
  17.         len2 = System.totalMemory;  
  18.         _txt.appendText("\n\nnew Shape后总内存大小为:" + len2.toString());  
  19.         _txt.appendText("\nnew Shape后增加的内存为:" + (len2 - len1).toString());  
  20.           
  21.         var rect:Rectangle = new Rectangle(0, 0, 200, 100);  
  22.         len1 = System.totalMemory;  
  23.         _txt.appendText("\n\nnew Rectangle,大小为200*100后总内存大小为:" + len1.toString());  
  24.         _txt.appendText("\nnew Rectangle,大小为200*100后增加的内存为:" + (len1 - len2).toString());  
  25.           
  26.         //注释掉下面一段,单独测试将sprite、bmp、shape三个空对象添加到显示列表。  
  27.         sprite.graphics.drawRect(0, 0, 200,100);  
  28.         len2 = System.totalMemory;  
  29.         _txt.appendText("\n\nsprite绘制200*100的矩形后总内存大小为:" + len2.toString());  
  30.         _txt.appendText("\nsprite绘制200*100的矩形后增加的内存为:" + (len2 - len1).toString());  
  31.         shape.graphics.drawRect(0,0,200,100)  
  32.         len1 = System.totalMemory;  
  33.         _txt.appendText("\n\nshape绘制200*100的矩形后总内存大小为:" + len1.toString());  
  34.         _txt.appendText("\nshape绘制200*100的矩形后增加的内存为:" + (len1 - len2).toString());  
  35.           
  36.         this.addChild(sprite);  
  37.         len2 = System.totalMemory;  
  38.         _txt.appendText("\n\n将sprite添加到舞台后总内存大小为:" + len2.toString());  
  39.         _txt.appendText("\nsprite添加到舞台后增加的内存为:" + (len2 - len1).toString());  
  40.         this.addChild(bmp);  
  41.         len1 = System.totalMemory;  
  42.         _txt.appendText("\n\n将bmp添加到舞台后总内存大小为:" + len1.toString());  
  43.         _txt.appendText("\nbmp添加到舞台后增加的内存为:" + (len1 - len2).toString());  
  44.         this.addChild(shape);  
  45.         len2 = System.totalMemory;  
  46.         _txt.appendText("\n\n将shape添加到舞台后总内存大小为:" + len2.toString());  
  47.         _txt.appendText("\nshape添加到舞台后增加的内存为:" + (len2 - len1).toString());  
  48. }  
private function compareDisplayObject():void{        len1 = System.totalMemory;        _txt.text = "测试前总内存大小为:" + len1.toString();                var sprite:Sprite = new Sprite();        len2 = System.totalMemory;        _txt.appendText("\nnew Sprite后总内存大小为:" + len2.toString());        _txt.appendText("\nnew Sprite后增加的内存为:" + (len2 - len1).toString());                var bmp:Bitmap = new Bitmap();        len1 = System.totalMemory;        _txt.appendText("\n\nnew Bitmap后总内存大小为:" + len1.toString());        _txt.appendText("\nnew Bitmap后增加的内存为:" + (len1 - len2).toString());                var shape:Shape = new Shape();        len2 = System.totalMemory;        _txt.appendText("\n\nnew Shape后总内存大小为:" + len2.toString());        _txt.appendText("\nnew Shape后增加的内存为:" + (len2 - len1).toString());                var rect:Rectangle = new Rectangle(0, 0, 200, 100);        len1 = System.totalMemory;        _txt.appendText("\n\nnew Rectangle,大小为200*100后总内存大小为:" + len1.toString());        _txt.appendText("\nnew Rectangle,大小为200*100后增加的内存为:" + (len1 - len2).toString());                //注释掉下面一段,单独测试将sprite、bmp、shape三个空对象添加到显示列表。        sprite.graphics.drawRect(0, 0, 200,100);        len2 = System.totalMemory;        _txt.appendText("\n\nsprite绘制200*100的矩形后总内存大小为:" + len2.toString());        _txt.appendText("\nsprite绘制200*100的矩形后增加的内存为:" + (len2 - len1).toString());        shape.graphics.drawRect(0,0,200,100)        len1 = System.totalMemory;        _txt.appendText("\n\nshape绘制200*100的矩形后总内存大小为:" + len1.toString());        _txt.appendText("\nshape绘制200*100的矩形后增加的内存为:" + (len1 - len2).toString());                this.addChild(sprite);        len2 = System.totalMemory;        _txt.appendText("\n\n将sprite添加到舞台后总内存大小为:" + len2.toString());        _txt.appendText("\nsprite添加到舞台后增加的内存为:" + (len2 - len1).toString());        this.addChild(bmp);        len1 = System.totalMemory;        _txt.appendText("\n\n将bmp添加到舞台后总内存大小为:" + len1.toString());        _txt.appendText("\nbmp添加到舞台后增加的内存为:" + (len1 - len2).toString());        this.addChild(shape);        len2 = System.totalMemory;        _txt.appendText("\n\n将shape添加到舞台后总内存大小为:" + len2.toString());        _txt.appendText("\nshape添加到舞台后增加的内存为:" + (len2 - len1).toString());}

再来看输出结果:
//------------------------------------------------>

测试前总内存大小为:3162112

new Sprite后总内存大小为:3162112
new Sprite后增加的内存为:0
new Bitmap后总内存大小为:3162112
new Bitmap后增加的内存为:0
new Shape后总内存大小为:3170304
new Shape后增加的内存为:8192
new Rectangle,大小为200*100后总内存大小为:3182592
new Rectangle,大小为200*100后增加的内存为:12288
sprite绘制200*100的矩形后总内存大小为:3190784
sprite绘制200*100的矩形后增加的内存为:8192
shape绘制200*100的矩形后总内存大小为:3198976
shape绘制200*100的矩形后增加的内存为:8192
将sprite添加到舞台后总内存大小为:3207168
sprite添加到舞台后增加的内存为:8192
将bmp添加到舞台后总内存大小为:3211264
bmp添加到舞台后增加的内存为:4096
将shape添加到舞台后总内存大小为:3215360
shape添加到舞台后增加的内存为:4096

//------------------------------------------------>

前天测试时,每次运行FP,第一次基本上new Sprite,Bitmap,Shape后增加的内存都不为0,前二者都是4096,Shape是8192,但今天每次重新运行前面两个对象new 后内存增加都是0。

运行以后,多次调用这个函数,输出的内容可以看到内存每次重复调用时都在之前基础上增加,但这几个new 操作之后,内存的增加经常0,而Shape为0的时候很少,多数是4096,偶尔是8192。
我猜测,这应该是运行时环境认为new 出来的实例,没有使用,并且引用计数也是0,所以被回收了,这从输出内容的后面一些部分(对象被添加到显示列表中),所显示的内存增加始终大于0可以看出来。
但是反复调用这个函数时,将sprite实例添加到显示列表中,使得增加的内存数却经常在4096和8192之间变动,这让我感到很是疑惑,想不到合理的解释。

二、对sprite,bitmap,shape  new 一批空对象,及将一批空对象添加到显示列表的内存占用测试

这里说的空对象不是指null的无值情况,而是仅指new之后,不加任何处理或赋值的情况。
先来看测试代码:

[javascript]
  1. private function compareMultiDisplayObject():void  
  2. {  
  3.         //每类对象new的个数   
  4.         var num:uint = 1000;                          
  5.           
  6.         len1 = System.totalMemory;  
  7.         _txt.text = "每次创建" + num.toString() + "个同类对象,测试前总内存大小为:" + len1.toString();  
  8.           
  9.         var i:uint;  
  10.         var sprite:Sprite;  
  11.         var bmp:Bitmap;  
  12.         var shape:Shape;  
  13.           
  14.         for (i = 0; i < num; i++)   
  15.         {  
  16.                 sprite = new Sprite();  
  17.                   
  18.                 //恢复以下注释,让new出来的对象有引用计数,避免对象被认为无使用而被回收  
  19.                 //arr.push(sprite);  
  20.         }  
  21.         len2 = System.totalMemory;  
  22.         _txt.appendText("\nnew Sprite后总内存大小为:" + len2.toString());  
  23.         _txt.appendText("\nnew Sprite后增加的内存为:" + (len2 - len1).toString() + " || 平均每个增加:" + ((len2 - len1)/num).toString());  
  24.           
  25.           
  26.         for (i = 0; i < num; i++)   
  27.         {  
  28.                 bmp = new Bitmap();  
  29.                 //arr.push(bmp);  
  30.         }                          
  31.         len1 = System.totalMemory;  
  32.         _txt.appendText("\n\nnew Bitmap后总内存大小为:" + len1.toString());  
  33.         _txt.appendText("\nnew Bitmap后增加的内存为:" + (len1 - len2).toString() + " || 平均每个增加:" + ((len1 - len2)/num).toString());  
  34.           
  35.         for (i = 0; i < num; i++)   
  36.         {  
  37.                 shape = new Shape();  
  38.                 //arr.push(shape);   
  39.         }                          
  40.         len2 = System.totalMemory;  
  41.         _txt.appendText("\n\nnew Shape后总内存大小为:" + len2.toString());  
  42.         _txt.appendText("\nnew Shape后增加的内存为:" + (len2 - len1).toString() + " || 平均每个增加:" + ((len2 - len1)/num).toString());  
  43.           
  44.         _txt.appendText("\n\n================以下为创建并添加到显示列表================");  
  45.           
  46.         for (i = 0; i < num; i++)   
  47.         {  
  48.                 sprite = new Sprite();  
  49.                 this.addChild(sprite);  
  50.         }  
  51.         len1 = System.totalMemory;  
  52.         _txt.appendText("\nnew Sprite后总内存大小为:" + len1.toString());  
  53.         _txt.appendText("\nnew Sprite后增加的内存为:" + (len1 - len2).toString() + " || 平均每个增加:" + ((len1 - len2)/num).toString());  
  54.           
  55.           
  56.         for (i = 0; i < num; i++)   
  57.         {  
  58.                 bmp = new Bitmap();  
  59.                 this.addChild(bmp);  
  60.         }                          
  61.         len2 = System.totalMemory;  
  62.         _txt.appendText("\n\nnew Bitmap后总内存大小为:" + len2.toString());  
  63.         _txt.appendText("\nnew Bitmap后增加的内存为:" + (len2 - len1).toString() + " || 平均每个增加:" + ((len2 - len1)/num).toString());  
  64.           
  65.         for (i = 0; i < num; i++)   
  66.         {  
  67.                 shape = new Shape();  
  68.                 this.addChild(shape);  
  69.         }                          
  70.         len1 = System.totalMemory;  
  71.         _txt.appendText("\n\nnew Shape后总内存大小为:" + len1.toString());  
  72.         _txt.appendText("\nnew Shape后增加的内存为:" + (len1 - len2).toString() + " || 平均每个增加:" + ((len1 - len2)/num).toString());  
  73. }  
private function compareMultiDisplayObject():void{        //每类对象new的个数        var num:uint = 1000;                                        len1 = System.totalMemory;        _txt.text = "每次创建" + num.toString() + "个同类对象,测试前总内存大小为:" + len1.toString();                var i:uint;        var sprite:Sprite;        var bmp:Bitmap;        var shape:Shape;                for (i = 0; i < num; i++)         {                sprite = new Sprite();                                //恢复以下注释,让new出来的对象有引用计数,避免对象被认为无使用而被回收                //arr.push(sprite);        }        len2 = System.totalMemory;        _txt.appendText("\nnew Sprite后总内存大小为:" + len2.toString());        _txt.appendText("\nnew Sprite后增加的内存为:" + (len2 - len1).toString() + " || 平均每个增加:" + ((len2 - len1)/num).toString());                        for (i = 0; i < num; i++)         {                bmp = new Bitmap();                //arr.push(bmp);        }                                len1 = System.totalMemory;        _txt.appendText("\n\nnew Bitmap后总内存大小为:" + len1.toString());        _txt.appendText("\nnew Bitmap后增加的内存为:" + (len1 - len2).toString() + " || 平均每个增加:" + ((len1 - len2)/num).toString());                for (i = 0; i < num; i++)         {                shape = new Shape();                //arr.push(shape);        }                                len2 = System.totalMemory;        _txt.appendText("\n\nnew Shape后总内存大小为:" + len2.toString());        _txt.appendText("\nnew Shape后增加的内存为:" + (len2 - len1).toString() + " || 平均每个增加:" + ((len2 - len1)/num).toString());                _txt.appendText("\n\n================以下为创建并添加到显示列表================");                for (i = 0; i < num; i++)         {                sprite = new Sprite();                this.addChild(sprite);        }        len1 = System.totalMemory;        _txt.appendText("\nnew Sprite后总内存大小为:" + len1.toString());        _txt.appendText("\nnew Sprite后增加的内存为:" + (len1 - len2).toString() + " || 平均每个增加:" + ((len1 - len2)/num).toString());                        for (i = 0; i < num; i++)         {                bmp = new Bitmap();                this.addChild(bmp);        }                                len2 = System.totalMemory;        _txt.appendText("\n\nnew Bitmap后总内存大小为:" + len2.toString());        _txt.appendText("\nnew Bitmap后增加的内存为:" + (len2 - len1).toString() + " || 平均每个增加:" + ((len2 - len1)/num).toString());                for (i = 0; i < num; i++)         {                shape = new Shape();                this.addChild(shape);        }                                len1 = System.totalMemory;        _txt.appendText("\n\nnew Shape后总内存大小为:" + len1.toString());        _txt.appendText("\nnew Shape后增加的内存为:" + (len1 - len2).toString() + " || 平均每个增加:" + ((len1 - len2)/num).toString());}

 

前一部分测试是创建一批空对象,不作任何其他的操作,那么这些对象的引用计数应该是0

后面一部分测试是创建一批空对象,并将创建的每个对象随后添加到了显示列表,那么这批对象就同时有了引用计数,也即不对这些对象进行其他处理情况下的内存占用。但对Bitmap对象还是略有不同,因为并有为其new 一个bitmapData对象实例,所以bitmap所占内存这里输出的要比实际的低
下面来看测试输出:

//------------------------------------------------------------------------------->

每次创建1000个同类对象,测试前总内存大小为:3158016

new Sprite后总内存大小为:3665920
new Sprite后增加的内存为:507904 || 平均每个增加:507.904
new Bitmap后总内存大小为:3788800
new Bitmap后增加的内存为:122880 || 平均每个增加:122.88
new Shape后总内存大小为:4141056
new Shape后增加的内存为:352256 || 平均每个增加:352.256
================以下为创建并添加到显示列表================
new Sprite后总内存大小为:3760128
new Sprite后增加的内存为:-380928 || 平均每个增加:-380.928
new Bitmap后总内存大小为:4505600
new Bitmap后增加的内存为:745472 || 平均每个增加:745.472
new Shape后总内存大小为:4886528
new Shape后增加的内存为:380928 || 平均每个增加:380.928

//-------------------------------------------------------------------------------->

可以看到输出中的这段:

//------------------------------------------------------------->

new Sprite后总内存大小为:3760128

new Sprite后增加的内存为:-380928

//------------------------------------------------------------->

内存增加是负值,如果看作是之前new 出的一批对象实例被回收了,重新创建一批Sprite实例,并添加到显示列表中,比之前仅创建Sprite而不添加到显示列表的内存“new Sprite后总内存大小为:3665920”相比来说,还算是比较合理的。

所以这个负值也就不意外了。
另外,从这个测试函数内的源码注释部分,可以看到“arr.push(sprite);”这样几行,是当时测试new 出来的这一批对象,不添加到显示列表,仅仅增加引用计数而进行的测试,可以与添加到显示列表的内存开销进行一下对比。

//-------------------------------------------------------------------------------->

每次创建1000个同类对象,测试前总内存大小为:3158016

new Sprite后总内存大小为:3670016
new Sprite后增加的内存为:512000 || 平均每个增加:512
new Bitmap后总内存大小为:4444160
new Bitmap后增加的内存为:774144 || 平均每个增加:774.144
new Shape后总内存大小为:4796416
new Shape后增加的内存为:352256 || 平均每个增加:352.256
================以下为创建并添加到显示列表================
new Sprite后总内存大小为:5373952
new Sprite后增加的内存为:577536 || 平均每个增加:577.536
new Bitmap后总内存大小为:6160384
new Bitmap后增加的内存为:786432 || 平均每个增加:786.432
new Shape后总内存大小为:6500352
new Shape后增加的内存为:339968 || 平均每个增加:339.968

//---------------------------------------------------------------------------------->

看两段输出,很清楚,可以对比着看,不解释。

三、对图片加载和位图常用操作的对比测试

先看测试函数,这个函数写的还早,测试的还早,所以对输出文字的处理跟前面两个有点不一样。

[javascript]
  1. private function compareBitmap():void  
  2. {  
  3.         var loader:Loader = new Loader();  
  4.         var path:String = "1.png";  
  5.           
  6.         var url:URLRequest = new URLRequest(path);  
  7.         loader.contentLoaderInfo.addEventListener(Event.COMPLETE, imageLoader_Complete);  
  8.         len2 = System.totalMemory;  
  9.         _txt.text = "载入图片前总内存大小为:" + len2.toString();  
  10.         loader.load(url);  
  11. }  
  12.   
  13. private function imageLoader_Complete(e:Event):void  
  14. {  
  15.         var text:String;  
  16.         len1 = System.totalMemory;  
  17.         text = _txt.text + "\n载入图片后总内存大小为:" + len1.toString();  
  18.           
  19.         text += "\n载入后增加的内存大小为:" + (len1 - len2).toString();  
  20.           
  21.         var loader:Loader = e.target.loader as Loader;  
  22.         var bmp:Bitmap = loader.content as Bitmap;  
  23.         text += "\n\nloader的总字节数为:" + loader.contentLoaderInfo.bytesTotal.toString();  
  24.         text += "\nBitmap的总字节数为:" + bmp.loaderInfo.bytesTotal.toString();  
  25.           
  26.         text += "\n当前总内存大小为:" + System.totalMemory.toString();  
  27.           
  28.         var bmpData:BitmapData = bmp.bitmapData;  
  29.         len2 = System.totalMemory;  
  30.         text += "\n\nBitmapData赋值后的总内存大小为:" + len2.toString();  
  31.         bmpData.copyPixels(bmp.bitmapData, new Rectangle(0,0,bmp.width,bmp.height), new Point(0, 0));  
  32.         len1 = System.totalMemory;  
  33.         text += "\ncopyPixels位图对象的bitmapData后的总内存大小为:" + len1.toString();  
  34.         bmpData.draw(bmp);  
  35.         len2 = System.totalMemory;  
  36.         text += "\nbitmapData.draw()位图对象后的总内存大小为:" + len2.toString();  
  37.         var bmp1:Bitmap = new Bitmap();  
  38.         text += "\nnew一个Bitmap空对象bmp1后的总内存大小为:" + System.totalMemory.toString();  
  39.         bmp1.bitmapData = bmpData;  
  40.         text += "\n对bmp1.bitmapData赋值后的总内存大小为:" + System.totalMemory.toString();  
  41.         var bmp2:Bitmap = new Bitmap(bmpData);  
  42.         text += "\n使用得到的bmpData对象生成bmp2后的总内存大小为:" + System.totalMemory.toString();  
  43.         this.addChild(bmp1);  
  44.         text += "\n将bmp1添加到显示列表后的总内存大小为:" + System.totalMemory.toString();  
  45.         this.addChild(bmp2);  
  46.         text += "\n将bmp2添加到显示列表后的总内存大小为:" + System.totalMemory.toString();  
  47.           
  48.         bmp1.x = 300;  
  49.         bmp1.y = 0;  
  50.         bmp2.x = 400;  
  51.         bmp2.y = bmp2.height + 10;  
  52.           
  53.         text += "\n\n图片宽度=" + bmp.width.toString() + ",高度=" + bmp.height.toString();  
  54.         text += "\n按PNG图片像素内存占用<ARGB*width*height>\n来计算,则纯像素占内存= 4 * " + bmp.width.toString() + " * " + bmp.height.toString() + " =" + (4 * bmp.width * bmp.height).toString();;  
  55.           
  56.         len2 = System.totalMemory;  
  57.         text += "\n\n当前总内存大小为:" + len2;  
  58.         bmpData = bmp.bitmapData.clone();  
  59.         len1 = System.totalMemory;  
  60.         text += "\n克隆位图对象的bitmapData后的总内存大小为:" + len1.toString();  
  61.         text += "\n克隆后增加的内存大小为:" + (len1 - len2).toString();  
  62.           
  63.         var num:uint = 1000;  
  64.         var j:uint;  
  65.         var tempBMP:Bitmap;  
  66.         var rect:Rectangle = new Rectangle(0, 0, bmp.width, bmp.height);  
  67.         var vertex:Point = new Point(0, 0);  
  68.         var dt:int = getTimer();  
  69.         len2 = System.totalMemory;  
  70.         text += "\n\nnum=" + num.toString() + " || 当前总内存大小为:" + len2;                          
  71.         for (j = 0; j < num; j++)   
  72.         {  
  73.                 tempBMP = new Bitmap();  
  74.                 //tempBMP.bitmapData = bmp.bitmapData;  
  75.                 tempBMP.bitmapData = new BitmapData(bmp.width, bmp.height);  
  76.                 tempBMP.bitmapData.copyPixels(bmp.bitmapData, rect, vertex);  
  77.                   
  78.                 this.addChild(tempBMP);  
  79.                 tempBMP.x = j + 300;  
  80.                 tempBMP.y = j;  
  81.         }  
  82.         len1 = System.totalMemory;  
  83.         dt = getTimer() - dt;  
  84.         text += "\n生成num个位图并添加到显示列表中后的总内存大小为:" + len1.toString();  
  85.         text += "\n生成num个位图并添加到显示列表中后增加的内存大小为:" + (len1 - len2).toString();  
  86.         text += "\n花费时间:" + dt.toString() + "毫秒 || 平均操作每个对象需时:" + (dt / num).toString();  
  87.         _txt.text = text;  
  88. }  
private function compareBitmap():void{        var loader:Loader = new Loader();        var path:String = "1.png";                var url:URLRequest = new URLRequest(path);        loader.contentLoaderInfo.addEventListener(Event.COMPLETE, imageLoader_Complete);        len2 = System.totalMemory;        _txt.text = "载入图片前总内存大小为:" + len2.toString();        loader.load(url);}private function imageLoader_Complete(e:Event):void{        var text:String;        len1 = System.totalMemory;        text = _txt.text + "\n载入图片后总内存大小为:" + len1.toString();                text += "\n载入后增加的内存大小为:" + (len1 - len2).toString();                var loader:Loader = e.target.loader as Loader;        var bmp:Bitmap = loader.content as Bitmap;        text += "\n\nloader的总字节数为:" + loader.contentLoaderInfo.bytesTotal.toString();        text += "\nBitmap的总字节数为:" + bmp.loaderInfo.bytesTotal.toString();                text += "\n当前总内存大小为:" + System.totalMemory.toString();                var bmpData:BitmapData = bmp.bitmapData;        len2 = System.totalMemory;        text += "\n\nBitmapData赋值后的总内存大小为:" + len2.toString();        bmpData.copyPixels(bmp.bitmapData, new Rectangle(0,0,bmp.width,bmp.height), new Point(0, 0));        len1 = System.totalMemory;        text += "\ncopyPixels位图对象的bitmapData后的总内存大小为:" + len1.toString();        bmpData.draw(bmp);        len2 = System.totalMemory;        text += "\nbitmapData.draw()位图对象后的总内存大小为:" + len2.toString();        var bmp1:Bitmap = new Bitmap();        text += "\nnew一个Bitmap空对象bmp1后的总内存大小为:" + System.totalMemory.toString();        bmp1.bitmapData = bmpData;        text += "\n对bmp1.bitmapData赋值后的总内存大小为:" + System.totalMemory.toString();        var bmp2:Bitmap = new Bitmap(bmpData);        text += "\n使用得到的bmpData对象生成bmp2后的总内存大小为:" + System.totalMemory.toString();        this.addChild(bmp1);        text += "\n将bmp1添加到显示列表后的总内存大小为:" + System.totalMemory.toString();        this.addChild(bmp2);        text += "\n将bmp2添加到显示列表后的总内存大小为:" + System.totalMemory.toString();                bmp1.x = 300;        bmp1.y = 0;        bmp2.x = 400;        bmp2.y = bmp2.height + 10;                text += "\n\n图片宽度=" + bmp.width.toString() + ",高度=" + bmp.height.toString();        text += "\n按PNG图片像素内存占用
\n来计算,则纯像素占内存= 4 * " + bmp.width.toString() + " * " + bmp.height.toString() + " =" + (4 * bmp.width * bmp.height).toString();; len2 = System.totalMemory; text += "\n\n当前总内存大小为:" + len2; bmpData = bmp.bitmapData.clone(); len1 = System.totalMemory; text += "\n克隆位图对象的bitmapData后的总内存大小为:" + len1.toString(); text += "\n克隆后增加的内存大小为:" + (len1 - len2).toString(); var num:uint = 1000; var j:uint; var tempBMP:Bitmap; var rect:Rectangle = new Rectangle(0, 0, bmp.width, bmp.height); var vertex:Point = new Point(0, 0); var dt:int = getTimer(); len2 = System.totalMemory; text += "\n\nnum=" + num.toString() + " || 当前总内存大小为:" + len2; for (j = 0; j < num; j++) { tempBMP = new Bitmap(); //tempBMP.bitmapData = bmp.bitmapData; tempBMP.bitmapData = new BitmapData(bmp.width, bmp.height); tempBMP.bitmapData.copyPixels(bmp.bitmapData, rect, vertex); this.addChild(tempBMP); tempBMP.x = j + 300; tempBMP.y = j; } len1 = System.totalMemory; dt = getTimer() - dt; text += "\n生成num个位图并添加到显示列表中后的总内存大小为:" + len1.toString(); text += "\n生成num个位图并添加到显示列表中后增加的内存大小为:" + (len1 - len2).toString(); text += "\n花费时间:" + dt.toString() + "毫秒 || 平均操作每个对象需时:" + (dt / num).toString(); _txt.text = text;}

对上面这段代码里面的for循环里面部分,以循环1000次为例,我进行三种测试。

[javascript]
  1. for (j = 0; j < num; j++)   
  2. {  
  3.         tempBMP = new Bitmap(bmp.bitmapData);  
  4.           
  5.         this.addChild(tempBMP);  
  6.         tempBMP.x = j + 300;  
  7.         tempBMP.y = j;  
  8. }  
for (j = 0; j < num; j++) {        tempBMP = new Bitmap(bmp.bitmapData);                this.addChild(tempBMP);        tempBMP.x = j + 300;        tempBMP.y = j;}

上面这种测试由载入的bitmapData直接生成要使用的位图,以方便添加到显示列表中使用。
上面这种测试输出是:

//------------------------------------------------------------------------->

载入图片前总内存大小为:3174400

载入图片后总内存大小为:3526656
载入后增加的内存大小为:352256
loader的总字节数为:61831
Bitmap的总字节数为:61831
当前总内存大小为:3526656
BitmapData赋值后的总内存大小为:3526656
copyPixels位图对象的bitmapData后的总内存大小为:3526656
bitmapData.draw()位图对象后的总内存大小为:3526656
new一个Bitmap空对象bmp1后的总内存大小为:3526656
对bmp1.bitmapData赋值后的总内存大小为:3526656
使用得到的bmpData对象生成bmp2后的总内存大小为:3526656
将bmp1添加到显示列表后的总内存大小为:3526656
将bmp2添加到显示列表后的总内存大小为:3526656
图片宽度=256,高度=255
按PNG图片像素内存占用<ARGB*width*height>
来计算,则纯像素占内存= 4 * 256 * 255 =261120
当前总内存大小为:3526656
克隆位图对象的bitmapData后的总内存大小为:3788800
克隆后增加的内存大小为:262144
num=1000 || 当前总内存大小为:3788800
生成num个位图并添加到显示列表中后的总内存大小为:4452352
生成num个位图并添加到显示列表中后增加的内存大小为:663552
花费时间:103毫秒 || 平均操作每个对象需时:0.103

//----------------------------------------------------------------------------->

再来看将for中代码修改为new出一个空bitmap之后,对其bitmapData赋值的做法,有没有不同。

[javascript]
  1. for (j = 0; j < num; j++)   
  2. {  
  3.         tempBMP = new Bitmap();  
  4.         tempBMP.bitmapData = bmp.bitmapData;  
  5.           
  6.         this.addChild(tempBMP);  
  7.         tempBMP.x = j + 300;  
  8.         tempBMP.y = j;  
  9. }  
for (j = 0; j < num; j++) {        tempBMP = new Bitmap();        tempBMP.bitmapData = bmp.bitmapData;                this.addChild(tempBMP);        tempBMP.x = j + 300;        tempBMP.y = j;}

 

请看输出:

//-------------------------------------------------------------------------------->

载入图片前总内存大小为:3174400

载入图片后总内存大小为:3526656
载入后增加的内存大小为:352256
loader的总字节数为:61831
Bitmap的总字节数为:61831
当前总内存大小为:3526656
BitmapData赋值后的总内存大小为:3526656
copyPixels位图对象的bitmapData后的总内存大小为:3526656
bitmapData.draw()位图对象后的总内存大小为:3526656
new一个Bitmap空对象bmp1后的总内存大小为:3526656
对bmp1.bitmapData赋值后的总内存大小为:3526656
使用得到的bmpData对象生成bmp2后的总内存大小为:3526656
将bmp1添加到显示列表后的总内存大小为:3526656
将bmp2添加到显示列表后的总内存大小为:3526656
图片宽度=256,高度=255
按PNG图片像素内存占用<ARGB*width*height>
来计算,则纯像素占内存= 4 * 256 * 255 =261120
当前总内存大小为:3526656
克隆位图对象的bitmapData后的总内存大小为:3788800
克隆后增加的内存大小为:262144
num=1000 || 当前总内存大小为:3788800
生成num个位图并添加到显示列表中后的总内存大小为:4456448
生成num个位图并添加到显示列表中后增加的内存大小为:667648
花费时间:17毫秒 || 平均操作每个对象需时:0.017

//------------------------------------------------------------------------------->

我之前试了很多次,这种处理,会比上面一种生成时直接以bitmapData来生成bitmap花费的时间要长一点,内存也多一点,不知这次如何,我修改代码后运行直接把输出复制过来了,也没细看。

提交以后看到前一次的输出,时间好久,喜欢动手的下载下面的RAR文件自己多测试几次吧,有时候测试效果不稳定,这很无奈。
下面来看另一种情况,new 出来bitmap后再new 出来一个bitmapData不填充值的对象,然后再利用其CopyPixels函数来处理,在看代码和效果前,一定明白这样相当于这个bitmap采用了一张新的位图,而CopyPixels函数,只是把另外一张位图中的数据重新指向过来。看完修改的代码和输出后,下面再细说。

[javascript]
  1. for (j = 0; j < num; j++)   
  2. {  
  3.         tempBMP = new Bitmap();  
  4.         tempBMP.bitmapData = new BitmapData(bmp.width, bmp.height);  
  5.         tempBMP.bitmapData.copyPixels(bmp.bitmapData, rect, vertex);  
  6.           
  7.         this.addChild(tempBMP);  
  8.         tempBMP.x = j + 300;  
  9.         tempBMP.y = j;  
  10. }  
for (j = 0; j < num; j++) {        tempBMP = new Bitmap();        tempBMP.bitmapData = new BitmapData(bmp.width, bmp.height);        tempBMP.bitmapData.copyPixels(bmp.bitmapData, rect, vertex);                this.addChild(tempBMP);        tempBMP.x = j + 300;        tempBMP.y = j;}

 

请看输出:

//------------------------------------------------------------------------------------>

载入图片前总内存大小为:3174400

载入图片后总内存大小为:3530752
载入后增加的内存大小为:356352
loader的总字节数为:61831
Bitmap的总字节数为:61831
当前总内存大小为:3530752
BitmapData赋值后的总内存大小为:3530752
copyPixels位图对象的bitmapData后的总内存大小为:3530752
bitmapData.draw()位图对象后的总内存大小为:3530752
new一个Bitmap空对象bmp1后的总内存大小为:3530752
对bmp1.bitmapData赋值后的总内存大小为:3530752
使用得到的bmpData对象生成bmp2后的总内存大小为:3530752
将bmp1添加到显示列表后的总内存大小为:3530752
将bmp2添加到显示列表后的总内存大小为:3530752
图片宽度=256,高度=255
按PNG图片像素内存占用<ARGB*width*height>
来计算,则纯像素占内存= 4 * 256 * 255 =261120
当前总内存大小为:3530752
克隆位图对象的bitmapData后的总内存大小为:3792896
克隆后增加的内存大小为:262144
num=1000 || 当前总内存大小为:3792896
生成num个位图并添加到显示列表中后的总内存大小为:266907648
生成num个位图并添加到显示列表中后增加的内存大小为:263114752
花费时间:1236毫秒 || 平均操作每个对象需时:1.236

//------------------------------------------------------------------------------------>

看看上面输出结果的最后两行,是不是很让人惊奇,好大的内存增加,以及好长的时间花费,CPU占用还好,我第一次测试时,num=10000,最后超过默认的15秒不响应限制,也没有出来结果,但CPU占用一直稳定在25%(仅FP,还是在debug模式下),而且操作系统提示虚拟内存不足。

从最后一个测试可以看出,我们要尽量少用new BitmapData()这个函数,如果使用了这个函数,基本相当于引入了一张新位图图片。
四、结论和我的猜测
结论将是由以上测试总结出的可验证的判断,但我的猜测就不行了,将仅仅是这些结论往前回溯性的猜测,所以可能被我蒙中了,也可能根本就是瞎猜,看到的各位,可以算做抛砖引玉,由此讨论,但不要就此抬扛。

1、通过前面对CopyPixels函数的测试,可以看到,这个函数是极节省内存的。与直接使用一个位图的bitmapData数据生成一个新的bitmap基本没有差别。而且速度也很不错。

//--------------------------------------------------------------->

num=1000 || 当前总内存大小为:3788800

生成num个位图并添加到显示列表中后的总内存大小为:4452352
生成num个位图并添加到显示列表中后增加的内存大小为:663552
花费时间:103毫秒 || 平均操作每个对象需时:0.103

//--------------------------------------------------------------->

这段输出是对循环1000次,先给bitmapData赋值,再调用CopyPixels的测试输出,看内存的增加与,跟去掉CopyPixels操作之后基本没有差别。

 

2、在使用位图资源时,尽可能的少用new BitmapData来生成位图,因为这相当于载入了一张新位图。

3、位图资源无论文件本身如何压缩,如何小,但载入内存后,其占用的内存空间,基本可按<ARGB*width*height>来计算,当然实际结果会比这个数值略大,这可以从测试中的Clon()函数的调用内存增加看出。

4、由结论3,可以推导出一个游戏的flash客户端中,究竟可以引入多少位图资源,按照webgame的内存占用率一半,CPU等计算资源30%的原则来算占用的最大内存,单独一个web game的内存占用量上限可考虑300MB~600MB,以轻量级的web game 上限内存300MB来算,那就是1024*1024分辩率的图片JPG之类无alpha通道的图片100张,PNG格式有alpha通道的图片75张,无论是矢量存储载入后转位图,还是直接就是位图。而这个位图资源的上限公式,可以作为一个游戏项目的主策划用来限制随意策划的一个测量标尺。

//---------------------------------------------------------------------------------------------------------------------->

以下是我猜测性的推论,请审慎视之

1、从内存增量上来看,使用已载入的位图资源数据来new 出一个新的位图,应该仅是生成了一个位图对象,而实际的位图数据还是指向原始的位图资源。
2、人为的在代码中调用垃圾回收机制,既没必要,也不需要,反而徒增系统资源的浪费。
3、普通对象的引用计数清除很重要,这关系到这些对象占用内存的回收。
4、位图类对象的位图数据,调用销毁函数很有必要,在不使用某个位图资源时,将某个位图资源手动销毁,垃圾回收并不起作用。
5、bitmapData相比普通的类对象,要特别一点,特别之处有二,一是该对象引用指向的对象实际数据区,需要手动销毁才能退出占用的内存;另一是对该对象引用指向的实际数据,在进行数据修改,以让其显示的位图有不同的显示效果时,该修改并不是直接在实际的原始数据上进行修改的。
6、从bitmapData的特别之处,可以看到,流布于一些AS3效率优化文章内的慎用滤镜等之类的看法,应该也是这个问题的延伸。

 

转载地址:http://mgjsi.baihongyu.com/

你可能感兴趣的文章
有return的情况下try catch finally的执行顺序(最有说服力的总结)
查看>>
String s1 = new String("abc"); String s2 = ("abc");
查看>>
JAVA数据类型
查看>>
Xshell 4 入门
查看>>
SoapUI-入门
查看>>
Oracle -常用命令
查看>>
JAVA技术简称
查看>>
ORACLE模糊查询优化浅谈
查看>>
2016——个人年度总结
查看>>
2017——新的开始,加油!
查看>>
【Python】学习笔记——-6.2、使用第三方模块
查看>>
【Python】学习笔记——-7.0、面向对象编程
查看>>
【Python】学习笔记——-7.1、类和实例
查看>>
【Python】学习笔记——-7.2、访问限制
查看>>
【Python】学习笔记——-7.3、继承和多态
查看>>
【Python】学习笔记——-7.4、获取对象信息
查看>>
【Python】学习笔记——-7.5、实例属性和类属性
查看>>
Linux设备模型(总线、设备、驱动程序和类)之四:class_register
查看>>
git中文安装教程
查看>>
虚拟机 CentOS7/RedHat7/OracleLinux7 配置静态IP地址 Ping 物理机和互联网
查看>>