1
votes

For some reason my app is using more and more memory as it runs, and I can't figure out how to dump old data I dont need anymore.

I use a Loader to load a .png I then use a BitmapData to store the image, so that I can go over and check each pixel and store the result.

Then I loop this x times.

When I start on the second run I don't need the old information anymore, but it looks like my app is still storing the data (the images loaded)..

Here's some of my code:

        public function loadImage():void{
            myLoader = new Loader();
            myLoader.contentLoaderInfo.addEventListener(Event.COMPLETE, imageLoaded);

            if (currentFrame.toString(10).length == 1){
                currentFrameURL = txtFolder.text + "\\" + txtImageName.text + txtImageNum.text.substr(0, txtImageNum.text.length - 1) + currentFrame + txtImageType.text;
            }
            if (currentFrame.toString(10).length == 2){
                currentFrameURL = txtFolder.text + "\\" + txtImageName.text + txtImageNum.text.substr(0, txtImageNum.text.length - 2) + currentFrame + txtImageType.text;
            }
            if (currentFrame.toString(10).length == 3){
                currentFrameURL = txtFolder.text + "\\" + txtImageName.text + txtImageNum.text.substr(0, txtImageNum.text.length - 3) + currentFrame + txtImageType.text;
            }
            trace(currentFrameURL + " sent to loader...");

            myLoader.load(new URLRequest(currentFrameURL));
        }

        public function imageLoaded(event:Event):void{
            myLoader.contentLoaderInfo.removeEventListener(Event.COMPLETE, imageLoaded);

            myBitmapData = new BitmapData(parseInt(txtWidth.text),parseInt(txtHeight.text),false);
            myBitmapData.draw(event.currentTarget.content);


            //Generate preview of current image being processed..
            //myPreviewImage.source = myBitmapData;
            labelProgress.text = "Current process: " + (currentFrame + 1) + "/" + (parseInt(txtFrames.text));


            for(var y:int=0; y < parseInt(txtHeight.text) ; y++){
                for(var x:int=0; x < parseInt(txtWidth.text) ; x++){
                    currentPixelColor = myBitmapData.getPixel(x,y);
                    myTabelClass.recordPixel(currentPixelColor);
                }
            }//ett bilde ferdig scannet og lagret

            currentFrame++;

            if(currentFrame < parseInt(txtFrames.text)){
                myTabelClass.newImg(currentFrame);
                trace("sending newImg command: " + currentFrame);
                loadImage();
            }else{
                //All frames done..
                //myTabelClass.traceResult();
                Alert.show("All images scanned!\n\nClick 'OK' to add new data to XML.", "Images scanned", Alert.OK, this, insertDataToXML);
                btnSave.enabled = true;
                btnTest1.enabled = true;
            }

        }
4

4 Answers

3
votes

Once you're done with myBitmapData, call myBitmapData.dispose() and myBitmapData = null. Keep in mind that after you call dispose(), your BitmapData object is not usable anymore, so you have to create a new instance (myBitmapData = new BitmapData()).

By the way, I did not analyze you code much, but I've noticed that you copied each image's bitmapData with draw() and then worked on it. You could work directly with loaded images content, that would save memory as well:

myBitmapData = Bitmap(event.target.content).bitmapData;

and don't forget to null each image before proceeding to the next frame if you do not intend to use them later.

2
votes

Unfortunately, there is a bug preventing Loader from ever freeing memory, see here: https://bugbase.adobe.com/index.cfm?event=bug&id=3071138 . As per my tests, it may be related to how fast do you want to dispose the previously loaded content, but this is a guess, don't take it for granted.

Other things: calling BitmapData.dispose() frees memory "immediately", but if you don't, then BitmapData instances are still GC'ed, however, that may take longer, and the loitering bitmaps may create an impression of a memory leak.

System.gc() is only available in debugger, which means that your end users will not be able to use it.

Setting variables, especially local variables to null, after the object is no longer used makes no sense, if the compiler was only a little smarter, it would probably identify it as a dead code and remove altogether. Setting data members to null is only useful if you actually want them to be null for whatever reason, which is as they call it, "a business logic reason", don't try to "help" the GC, all chances are you will do more harm then good. When I come across a code that assigns null, this usually lights a red bulb, it is rather a bad design, or redundancy. If your code is good, you shouldn't need to do it.

0
votes

You're in recursion, so yes, you don't release the preview image loaded.

Try to set myBitmapData = null before call loadImage() again.

If nothing happen you can try force a garbage collector by calling System.gc() before loadImage:

myBitmapData = null;
System.gc();
loadImage();

But thats not a good practice they say. :P

If nothing works just remove the recursion thing that may help.

0
votes

Other answers have covered how to deal with BitmapData but I've noticed that Loaders seem to hold on to memory as well. Unfortunately I cannot point to any specific sources other than my experience. You can give this a try to see what shakes out:

Move the line

myLoader = new Loader();

out of the loadImage() function and instantiate that variable in the class constructor. This will allow you to reuse the same Loader instance over and over again.