BitmapData.draw - disposal of data?

Hello everyone, new guy here.

I have a problem with strange memory allocation. I tried to illustrate the problem on a sample application, you’ll find it in the attached zip file, or you can open this page:

http://knives.tym.cz/tmp/flash/bmpdatatest/test.html

You’ll also need something to see the memory currently allocated to flash applications - I use WSMonitor, please open it here:

http://knives.tym.cz/tmp/flash/bmpdatatest/wsmonitor/WSMonitor.html

The code in the sample application is very simple. It does the following (please run the WSMonitor html, press “Start”, and open the test page from above in another tab/window):

  1. Download a file called “sample.swf”. This is a swf file generated from PDF using pdf2swf tools, I’m sure you know this utility. The swf looks fine, it contains some images, but for some reason it causes me trouble - see below.

  2. Once the file loads, I try to draw and display a bitmap representation of the loaded file, using bitmapdata and its draw function. So far so good. Note that I store the reference to the loaded MovieClip - I need it! Well not in this case, but say I might need it later.

  3. Once the bitmap is displayed, you can click in the application. This should result in disposing of the bitmap. However, if I watch the memory allocation using WSMonitor, the memory usage won’t drop down to the expected values - some tens of MB still remain. And that is the problem - say if I do this repeatedly with more swf files like this, I will have some hundreds MB of useless memory in my application - not cool.

From what I understand, the memory usage of this simple application goes as follows (let’s say that after the start of my application, the usage is 0).

After the swf is loaded, the usage goes to say +1 MB (reflecting the fact that I’ve downloaded some data).

After the bitmap is drawn, the usage jumps to say +40 MB (why so much?).

When I want to dispose of the data, then after I dispose of the BitmapData property of the bitmap and set the bitmap reference to null (and force garbage collector to work using the known hack), the usage goes down to say +35MB. Why not to +1 MB as after the file was downloaded? I can set the reference for the downloaded MovieClip to null, which will clear the memory as I’d like. However, as I said I will need the loaded movie later - so I can’t simply clear the whole MovieClip.

What am I doing wrong? I thought that the “dispose” method should get rid of the memory allocated to the bitmapdata instance? I don’t know why after the draw method some data remain (I guess) within the loaded MovieClip, and cannot be disposed of (at least I don’t know how) without destroying the whole MovieClip.

Another strange thing I can see this behaviour only with some files (some pages from converted PDF file). If the page contains mostly text, then there is none (none perceivable that is) jump in memory usage. But pages with raster images do usually result in this behaviour.

Is this normal, or can I get rid of the excess memory?

Thanks,
best regards

Pavel

PS: I put the code here for reference:


this.stage.scaleMode = StageScaleMode.NO_SCALE;
this.stage.align = StageAlign.TOP_LEFT;


var mLoader:Loader;
var req:URLRequest;

// let's download the sample swf
req = new URLRequest("./sample.swf");

mLoader = new Loader;
mLoader.contentLoaderInfo.addEventListener(Event.COMPLETE, onDocLoaded,false,0,true);
logArea.htmlText = 'starting download';
mLoader.load(req);

var doc:MovieClip = new MovieClip;
var bmp:Bitmap = null;

function onDocLoaded(e:Event)
{
    logArea.htmlText += '<br />file downloaded';
    logArea.htmlText += '<br />click to remove it';
    
    // store the loaded MovieClip
    doc = MovieClip(DisplayObject(mLoader.content));
    
    // draw and display the bitmap
    bmp = new Bitmap();
    bmp.bitmapData = new BitmapData(doc.width,doc.height);
    bmp.bitmapData.draw(doc);
    
    // just so that it fits the screen
    bmp.scaleX = 0.4;
    bmp.scaleY = 0.4;
    bmp.x = 50;
    bmp.y = 60;
    this.addChildAt(bmp,0);
    
    // this is just so that "doc" doesn't have "mLoader" as parent
    this.addChild(doc);
    this.removeChild(doc);
    
    // we don't need the mLoader anymore... We could store the loaded MovieClip in it, but it
    // is the same story as with the "doc" MovieClip...
    mLoader = null;
    
    this.stage.addEventListener(MouseEvent.CLICK,onStageClick);
}

function onStageClick(e:MouseEvent)
{
    logArea.htmlText += '<br />removing bitmap';
    
    bmp.bitmapData.dispose();
    this.removeChild(bmp);
    bmp = null;

    // uncomment this to see the memory drop down to the correct values
    //doc = null;
    
    // just to make sure the GC will do its thing
    gcHack();
}

function gcHack():void
{
    // unsupported hack that seems to force a full GC
    try{
        var lc1:LocalConnection = new LocalConnection();
        var lc2:LocalConnection = new LocalConnection();
        lc1.connect('name');
        lc2.connect('name');
    }
    catch (e:Error)
    {
        
    }

}