:::: 菜单 ::::

Loader的资源不释放

昨天遇到个内存泄露的情况。查到最后发现加载外部的一个.swf里面的倒影截的位图没清除导致的。
推测loader.unloadAndStop()虽然会关闭流,但是并不释放被加载的程序中产生的BitmapData。

切记:除了纯粹的试听对象,所加载的外部程序中有监听、回调、创建了BitmapData的,此外部程序在被unload前请自行了断。否则你再怎么gc都吃着内存。

解决方案:

这种情况,我一般让被加载的flash程序实现一个dispose方法,加载它的loader在unloadAndStop()前先try一下dispose();
外部程序在提供的dispose方法中自行了断。自行了断的步骤各不相同,需要根据自身在存活期间所作所为来有的放矢地消灭自我才行。总体来讲包括:斩断自己所相关的回调、监听,释放内存(声音关闭、视频清除、关闭网络连接、清除位图数据)。

就像这样,主文件中:

  1. private function closeExFlash() : void
  2.         {
  3.             try
  4.             {
  5.                 (exFlashLoader.content as IDispose).dispose();
  6.             }catch(e : *)
  7.             {
  8.             }
  9.             try
  10.             {
  11.                 exFlashLoader.unloadAndStop();
  12.             }catch(e : *)
  13.             {
  14.             }
  15.             this.visible = false;
  16.         }

还有一种方法:
被加载的Flash自己维护。这种方法只适用于文档类直接用来放到舞台的,我并不常这样做(我很多情况下把swf作为runtime sharelib用,加载进去只是需要其中一些类)——
被加载的Flash自己监听remove_from_stage事件,该事件触发时释放自己内存,上面说的斩断自己所相关的回调、监听,释放内存(声音关闭、视频清除、关闭网络连接、清除位图数据)。同时确保主程序中没有对它的引用。

延伸阅读


  1. 我也遇到這樣的問題了,雖然使用了unloadAndStop(),但我裏面用到的位圖的數據并沒有釋放內存了。我是使用loader來加載位圖的,怎麼才能清除位图数据,因為病沒有聲音了之類的。還是直接用樓主的這個。請樓主指點下了。謝謝!!

  2. 不要依赖unloadAndStop(),要自己在所有程序里实现dispose方法,移除的时候调用一下,每个程序自己释放自己所占用的内存。
    bmd是要自己去释放的,unloadAndStop不会帮你搞定。

  3. import flash.display.Bitmap;
    import flash.display.BitmapData;
    import flash.display.Loader;
    import flash.display.Sprite;
    import flash.events.Event;
    import flash.events.IOErrorEvent;
    import flash.net.URLLoader;
    import flash.net.URLRequest;
    class Test extends Sprite
    {

    private var loader:Loader;
    private var targetContainer:Sprite;
    private var listenersList:Array;
    public function Test(flieUrl:String,targetContainer:Sprite)
    {
    //初始化數組
    listenersList = new Array();
    //偵聽刪除事件
    addEventListener (Event.REMOVED,remove);

    this.targetContainer = targetContainer;

    loader=new Loader();
    //var variables:URLLoader = new URLLoader();

    //variables.dataFormat = URLLoaderDataFormat.BINARY;

    //variables.addEventListener (Event.COMPLETE,loadComplet);

    loader.contentLoaderInfo.addEventListener (Event.COMPLETE,loadComplet);
    loader.contentLoaderInfo.addEventListener (IOErrorEvent.IO_ERROR,ioErrorHandler);
    loader.load (new URLRequest(flieUrl));
    }
    private function loadComplet(evt:Event)
    {
    addChild(loader);

    //var content:ByteArray = evt.target.data as ByteArray;
    //loader.loadBytes(content);

    this.targetContainer.addChild(this);
    loader.contentLoaderInfo.removeEventListener (Event.COMPLETE,loadComplet);
    }
    private function ioErrorHandler(evt:IOErrorEvent)
    {
    var bg = new BG();
    addChild(bg);

    this.targetContainer.addChild(this);
    /*this.graphics.clear();
    this.graphics.beginFill(0x000000,1);
    this.graphics.lineStyle(1,0×000000,0);
    this.graphics.drawRect(0,0,this.stage.stageWidth,this.stage.stageHeight);
    this.graphics.endFill();
    */

    loader.contentLoaderInfo.removeEventListener (IOErrorEvent.IO_ERROR,ioErrorHandler);
    }
    //自我毀滅的方法
    private function remove (e:Event):void
    {
    this.targetContainer=null;
    if (e.currentTarget != e.target)
    {
    return;
    }
    //刪除子對象
    //trace (“刪除前所有子對象”,numChildren);
    while (numChildren > 0)
    {
    removeChildAt (0);
    }
    //trace (“刪除后所有子對象”,numChildren);
    //刪除偵聽
    for (var i:uint=0; i<listenersList.length; i++)
    {
    //trace ("刪除Listener",lis[i][0]);
    removeEventListener (listenersList[i][0],listenersList[i][1],listenersList[i][2]);
    }
    listenersList = null;
    (loader.content as Bitmap).bitmapData.dispose();
    loader.unloadAndStop();
    loader = null;
    }
    }
    多謝炸彈的回覆,在remove這個裏面我也做了(loader.content as Bitmap).bitmapData.dispose();這樣的動作。按道理應該是釋放掉位圖的內存的。我發現現在這樣處理了比內存不這樣處理的情況下增加的小了,每次刪除Test這個對象的實例后在新建一個再load新的圖的時候,內存的佔有就會增加。說明我上次的位圖的部份內存還是沒有清除掉了。如果我使用flash裏面畫的純色背景的話,內存就不會上漲了,所以我判斷畫你是load進來的位圖的內存沒有清掉了。但是除了上面的方法,我實在不知道還有什麽方法了。
    再有你是上面code中exFlashLoader.content as IDispose在as中就沒有IDispose這個東西,所以我就用了(loader.content as Bitmap).bitmapData.dispose();不知道是不是這個上面有點出入。請炸彈幫我看下了。謝謝!!
    ps:來這我發現一些實實在在的東西,學習了。

  4. @qweasz, loader.content as Bitmap,问题是往往我们加载来的loader.content不是BMP的,那种你这样就不适用了。
    IDispose是我自己的接口,只要是被加载到别的程序中的程序,都必须实现此接口,也就是都提供dispose()方法,在此方法中释放自己的内存。
    比如对应你的情况,你应该把要加载的文件(flieUrl的目标)实现IDispose接口,如果你加载的是别人的文件,那就没办法了。

  5. @鼠标炸弹,
    是啊,我加載的是JPG的,那這樣加沒有辦法釋放內存了嗎?用的是lite4,好加載進去的jpg也會轉化為bmp的。因為按照jpg的大小來看只有100多kb,但加載一張圖所使用的內存進去后確實jpg另存為bmp的大小。使用這個后比原來不使用內存增加要少了,但與相比較用純色矢量背景來說(這個內存不會增長)還是有什麽東西沒有卸載掉了。

  6. @qweasz, 加载jpg的话应该不存在这个问题啊,你要是找到问题的原因,一定要指点指点我们哦。

  7. loader還有什麽資源,向我現在已經清楚掉了位圖的資源,但還是有些東西在殘留。因為只加載矢量圖的話確實沒有變化內存。問題還是loader的問題了。

  8. @qweasz,
    最後發現是跟播放器有關係,釋放bitmapData內存的方法沒有錯了。