views:

173

answers:

4

I'm having some trouble with memory management in a flash app. Memory usage grows quite a bit, and I've tracked it down to the way I load assets.

I embed several raster images in a class Embedded, like this

[Embed(source="/home/gabriel/text_hard.jpg")]
public static var ASSET_text_hard_DOT_jpg : Class;

I then instance the assets this way

var pClass : Class = Embedded[sResource] as Class;
return new pClass() as Bitmap;

At this point, memory usage goes up, which is perfectly normal. However, nulling all the references to the object doesn't free the memory.

Based on this behavior, looks like the flash player is creating an instance of the class the first time I request it, but never ever releases it - not without references, calling System.gc(), doing the double LocalConnection trick, or calling dispose() on the BitmapData objects.

Of course, this is very undesirable - memory usage would grow until everything in the SWFs is instanced, regardless of whether I stopped using some asset long ago.

Is my analysis correct? Can anything be done to fix this?

A: 

Hi,

Why do you want to use this ???

var pClass : Class = Embedded[sResource] as Class; 
return new pClass() as Bitmap;

Sometimes dynamically resource assignment is buggy and fails to be freed up. I had similar problems before with flash player and flex, for ex. loading and unloading the same external swf... the memory was increasing constantly with the size of the loaded swf without going down even if I was calling the system.gc(); after unloading the swf.

So my suggestion is to skip this approach and use the first case you have described.


UPDATE 1

<?xml version="1.0" encoding="utf-8"?>
<s:Application 
    xmlns:fx           = "http://ns.adobe.com/mxml/2009" 
    xmlns:s         = "library://ns.adobe.com/flex/spark" 
    xmlns:mx           = "library://ns.adobe.com/flex/mx" 
    creationComplete   = "creationComplete()">

<fx:Script>
    <![CDATA[



        [Embed(source="/assets/logo1w.png")]
        private static var asset1:Class;

        [Embed(source="/assets/060110sinkhole.jpg")]
        private static var asset2:Class; 


        private static var _dict:Dictionary = new Dictionary();

        private static function initDictionary():void
        {
            _dict["/assets/logo1w.png"] = asset1;
            _dict["/assets/060110sinkhole.jpg"] = asset2;
        }

        public static function getAssetClass(assetPath:String):Class
        {
            // if the asset is already in the dictionary then just return it
            if(_dict[assetPath] !=  null)
            {
                return _dict[assetPath] as Class; 
            }

            return null;
        }

        private function creationComplete():void
        {
            initDictionary();
            var asset1:Class = getAssetClass("/assets/logo1w.png");
            var asset2:Class = getAssetClass("/assets/060110sinkhole.jpg");
            var asset3:Class = getAssetClass("/assets/logo1w.png");
            var asset4:Class = getAssetClass("/assets/060110sinkhole.jpg");
            var asset5:Class = getAssetClass("/assets/logo1w.png");
        }
    ]]>
</fx:Script>

Adrian Pirvulescu
Sorry, I don't quite follow. I described only one way of doing it. The first snippet shows how I embed the assets, and the second how I instance them. Do you mean why don't I just do `new ASSET_text_hard_DOT_jpg()`? That's because my code is quite complex and doesn't always know in advance it needs, for example it creates a string from several pieces and gets that named image (ie "text_" + s_lDifficulty[nDifficulty] -> "text_hard")
ggambett
I am sorry but I think the thing you are trying to achieve is not possible. From what I know resources are embedded when the swf is built, I am not sure that you can embed a resource dynamically as you described.What I would do it to create a dictionary and put there all the resources I need. I would use it like in the UPDATE1 code I wrote now in the response.
Adrian Pirvulescu
+1  A: 

Make sure you run your tests again in the non-debug player. Debug player doesn't always reclaim all the memory properly when releasing assets.

Also, because you're using an Embedded rather than loaded asset, the actual data might not ever be released. As it's part of your SWF, I'd say you could reasonably expect it to be in memory for the lifetime of your SWF.

Sophistifunk
A: 

At this point, memory usage goes up, which is perfectly normal. However, nulling all the references to the object doesn't free the memory.

That's also perfectly normal. It's rare for any system to guarantee that the moment you stop referencing an object in the code is the same moment that the memory for it is returned to the operating system. That's exactly why methods like System.gc() are there, to allow you to force a clean-up when you need one. Usually the application may implement some sort of pooling to keep around objects and memory for efficiency purposes (as memory allocation is typically slow). And even if the application does return the memory to the operating system, the OS might still consider it as assigned to the application for a while, in case the app needs to request some more shortly afterwards.

You only need to worry if that freed memory is not getting reused. For example, you should find that if you create an object, free it, and repeat this process, memory usage should not grow linearly with the number of objects you create, as the memory for previously freed objects gets reallocated into the new ones. If you can confirm that does not happen, edit your question to say so. :)

Kylotan
A: 

It turns out I was keeping references to the objects that didn't unload. Very tricky ones. The GC does work correctly in all the cases I described, which I suspected may have been different.

More precisely, loading a SWF, instancing lots of classes defined in it, adding them to the display list, manipulating them, and then removing all references and unloading the SWF leaves me with almost exactly the same memory I started with.

ggambett