【问题标题】:AS3 drawing Sprite on BitmapData memory leakAS3 在 BitmapData 上绘制 Sprite 内存泄漏
【发布时间】:2017-11-07 11:50:49
【问题描述】:

我有一个 AIR 应用程序,它可以从 Flash 编辑器呈现图像。您可以自定义多个表面 - 它们都具有相同的宽度和高度。然后每个表面都由 AIR 应用程序渲染。

我正在努力解决我无法解决的内存泄漏问题。

对于我需要渲染的每个表面,我都有一个包含许多组件的 Sprite(其他 sprite - 一些具有事件侦听器、BitmapDatas 和其他子组件、sprite 等)。

我已经知道要被垃圾收集的 BitmapData 问题,我都测试了:

  • 为每次渲染创建一个新的 BitmapData 然后 dispose() 并指向 null
  • 为每次渲染重复使用单个 BitmapData

内存泄漏仍然以相同的比例发生。

这是我的循环:

var bm:BitmapData = new BitmapData(destDim.x, destDim.y, true, bgColor);
var mtx:Matrix = new Matrix();
trace('before drawing :'+(System.privateMemory/1024));
bm.draw(myBigSprite, mtx, null, null, null, true);
trace('after drawing :'+(System.privateMemory/1024));
var result:Bitmap = new Bitmap(bm, PixelSnapping.NEVER, true);

//return result and encode Bitmap to png

result.bitmapData.dispose();
result.bitmapData = null;
result = null;

结果:

before drawing :208364
after drawing :302816
Wrote bitmap to file: surface0.png
before drawing :303296 
after drawing :446160 
Wrote bitmap to file: surface1.png 
before drawing :446160
after drawing :565212
Wrote bitmap to file: surface2.png 
before drawing :565924
after drawing :703100 
Wrote bitmap to file: surface3.png 
before drawing :703572
after drawing :834420 
Wrote bitmap to file: surface4.png 

我觉得我在绘制函数行为中遗漏了一些东西。似乎我新创建了 myBigSprite 组件的实例,这些实例在绘制操作后仍然存在。

我试图在每个循环结束时完全销毁 myBigSprite,它不会改变任何东西......

任何提示将不胜感激!

【问题讨论】:

  • 你怎么知道垃圾回收进程运行了?你强迫它吗?也只是作为旁注,据我所知,通过调用 System.gc() 强制 gc 仅适用于调试播放器。
  • 正如你所见,一个绘制操作结束和下一个开始之间的内存几乎是相等的,但是在这之间有很多内存消耗操作。特别是将位图编码为png。所以我假设 GC 进程正在完成它的工作,因为我想要清理的所有资源都被清理了,除了绘制函数。我从不明确调用 System.gc()。
  • 您是否将“结果”设置为 null,因为它包含对位图数据的引用?
  • 是的,我编辑了我的 sn-p,因为不清楚,抱歉。
  • @Bedu33 通常情况下,垃圾收集器是惰性的,不会在未使用时立即处理任何未使用的内存。因此,我建议您尝试显式调用 System.gc() 并查看内存测量值。如果这样做可以解决你的问题,那么它不是内存泄漏,它只是 GC 懒惰。

标签: actionscript-3 flash memory-leaks air


【解决方案1】:

好的,伙计们,我最终理解并解决了这个问题。

首先,我安装并运行了 Adob​​e Scout。很棒的工具。

您可能看不到(加上它是法语),我生成了 3 个与边缘相对应的曲面。右侧的“大”绿色条消耗大量内存,代表“位图显示对象”。有趣的 !以前从未听说过。

后来谷歌搜索,发现这篇文章:https://help.adobe.com/en_US/as3/dev/WS5b3ccc516d4fbf351e63e3d118a9b90204-7e26.html

解释

例如,在前面显示的代码摘录中,一旦加载 pict Loader 对象的操作完成后,pict 对象将 加载一个子显示对象,即位图。访问 这个位图显示对象,可以写pict.getChildAt(0)。

所以我开始不明白,不知何故,也许 Bitmap 对象作为子对象附加在 myBigSprite 的某些对象上。

最后,我创建了一个递归函数,在draw 操作之后搜索并销毁myBigSprite 中包含的所有BitmapBitmapDataByteArray 对象

//inside render function
bm.draw(myBigSprite, mtx, null, null, null, true);
destroyDisplayObjects(myBigSprite);

...

private function destroyDisplayObjects(obj):void{
    if ("numChildren" in obj){
        for (var i:int = 0; i<obj.numChildren; i++)
        {
            destroyDisplayObjects(obj.getChildAt(i));
        }
    }
    else {
        if (flash.utils.getQualifiedClassName(obj) == "flash.display::Bitmap"){
            //trace ('FREE BITMAP');
            obj.bitmapData.dispose();
            obj.bitmapData = null;
            obj = null;
            return;
        }
        else if (flash.utils.getQualifiedClassName(obj) == "flash.display::BitmapData"){
            //trace ('FREE BITMAPDATA');
            obj.dispose();
            obj = null;
            return;
        }
        else if (flash.utils.getQualifiedClassName(obj) == "flash.display::ByteArray"){
            //trace ('FREE BYTEARRAY');
            obj.clear();
            obj = null;
            return;
        }

        return;
    }
}

等等,在绘制操作后内存被 100% 清理,不再泄漏:)

【讨论】:

    【解决方案2】:

    您应该在任何函数之外声明 BitmapBitmapData,然后简单地回收它们以在循环内使用(而不是创建 new 任何要添加到内存中的东西)。

    当您确定不再需要来自bm 变量的位图数据时,仅在最后一张图像上使用.dispose()。否则,如果处置,您将不得不再次创建一个新的替代 var someThing :BitmapData = new BitmapData 以供进一步使用。

    ////# declare globally (not inside some specific function..)
    
    //var destDim :Point = new Point(your_X_num , your_Y_num);
    //var bgColor :uint = 0x505050;
    
    var bm:BitmapData = new BitmapData(destDim.x, destDim.y, true, bgColor);
    var result:Bitmap = new Bitmap(bm, PixelSnapping.NEVER, true);
    //result.bitmapData = bm; //can be set here but done within function for clarity...
    
    var mtx:Matrix = new Matrix();
    
    
    ////# update bitmap by replacing its bitmapdata with new colour values of existing pixels
    
    function Here_is_my_loop (): void 
    {
        trace('before drawing :'+(System.privateMemory/1024));
    
        //overwrite pixel colours in bitmap (result)
        bm.draw(myBigSprite, mtx, null, null, null, true);
        result.bitmapData = bm; //update bitmap
    
        trace('after drawing :'+(System.privateMemory/1024));
    
    
        //return result and encode Bitmap to png
    
        //result.bitmapData.dispose();
        //result.bitmapData = null;
        //result = null;
    }
    

    【讨论】:

    • 感谢您的回答。不幸的是,正如我在原始帖子中所说,我使用重用的 BitmapData 得到了完全相同的结果。但是,我再次尝试使用您的解决方案,并确认它具有相同的结果。
    • 嗯,不知道上次我怎么错过了“为每次渲染重复使用单个 BitmapData”,但是,在循环中使用 new 是消耗内存的好方法。我的代码应该只是覆盖 已经存在的数据,所以无法理解它是如何以某种方式添加 更多新数据(增加内存使用量)。我会调查是否可以使用 Flash。
    • “我的代码应该只是覆盖已经存在的数据,所以无法理解如何以某种方式添加更多新数据(增加内存使用量)”这也是我的问题:)
    猜你喜欢
    • 2012-08-03
    • 1970-01-01
    • 2012-11-28
    • 2018-01-22
    • 1970-01-01
    • 1970-01-01
    • 2010-10-14
    • 1970-01-01
    • 2012-08-22
    相关资源
    最近更新 更多