Question

I have the following code:

private var xmlC:XMLListCollection = new XMLListCollection();
private var httpS:HTTPService = new HTTPService();
private var timer:Timer = new Timer(1000);
private var xmlData:XML;
private var xmlDataList:XMLList;

protected function application1_creationCompleteHandler(event:FlexEvent):void
{
    httpS.url = "data.xml";
    httpS.addEventListener(ResultEvent.RESULT, resultHTTP);
    httpS.resultFormat="e4x";
    httpS.send();

    timer.start();
    timer.addEventListener(TimerEvent.TIMER, updateXMLC);
}

private function updateXMLC(event:TimerEvent):void
{
    xmlC.source = xmlDataList;
    httpS.send();
}

private function resultHTTP(event:ResultEvent):void
{
    xmlData = event.result as XML;
    xmlDataList = xmlData.dg.rows.row;
}

"data.xml" have 5000 lines, so I need to clean its traces whenever necessary. I have two problems that I found thanks to the profiling

  1. Every time httpS.send() is called in the method updateXMLC, it calls URLLoader internally which keeps XML that is not needed anymore wandering in the memory without being garbage collected
  2. is there a more effective way to update xmlC, whenever the XMLListCollection is updated, it seems that the previous value of XMLListCollection doesn't get garbage collected

OTHER TIPS

I was skeptical/intrigued after looking at the code that there would be any memory leaks so I tested out your code for myself.

The code you posted isn't actually run able. I just added the bare minimum to have it run like app tag and import statements (and a counter label to see when a http service cycle completes):

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" creationComplete="application1_creationCompleteHandler(event)"> 
<mx:Script>
    <![CDATA[
        import mx.rpc.http.HTTPService;
        import mx.collections.XMLListCollection;
        import mx.rpc.events.ResultEvent;
        import mx.events.FlexEvent;

        private var xmlC:XMLListCollection = new XMLListCollection();
        private var httpS:HTTPService = new HTTPService();
        private var timer:Timer = new Timer(1000);
        private var xmlData:XML;
        private var xmlDataList:XMLList;

        protected function application1_creationCompleteHandler(event:FlexEvent):void
        {
            httpS.url = "data.xml";
            httpS.addEventListener(ResultEvent.RESULT, resultHTTP);
            httpS.resultFormat="e4x";
            httpS.send();

            timer.start();
            timer.addEventListener(TimerEvent.TIMER, updateXMLC);
        }

        private function updateXMLC(event:TimerEvent):void
        {
            xmlC.source = xmlDataList;
            httpS.send();
        }

        private function resultHTTP(event:ResultEvent):void
        {
            counter.text = Number(parseInt(counter.text,10)+1).toString(); 
            xmlData = event.result as XML;
            xmlDataList = xmlData.dg.rows.row;
        }
    ]]>
</mx:Script>
<mx:Label id="counter" text="0" horizontalCenter="0" verticalCenter="0" fontSize="72"/>
</mx:Application>

The good news is there aren't any memory leaks in your code. There also aren't any loitering objects in profiler either.

The bad news is whatever else you're doing in your application (code that is not posted up here, omitted for whatever reason) - that is where you have a leak / loitering objects.

You can see for yourself in profiler of the attached code, that the memory doesn't increase after the first few cycles of the http service. I.e. it doesn't continue grasp more memory over time. (By the way, the XML file I'm pulling in is about 8000 rows, over 1MB).

If you'd like to post more code, happy to look further - but think this solves this mystery for now. ;)

Here's some fixes for what your doing:

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" creationComplete="application1_creationCompleteHandler(event)">
<mx:Script>
    <![CDATA[
        import mx.rpc.http.HTTPService;
        import mx.collections.XMLListCollection;
        import mx.rpc.events.ResultEvent;
        import mx.events.FlexEvent;

        [Bindable] private var xmlC:XMLListCollection = new XMLListCollection();
        private var httpS:HTTPService = new HTTPService();
        private var timer:Timer = new Timer(1000);
        private var xmlData:XML;
        private var xmlDataList:XMLList;
        private var serviceRunning : Boolean = false;
        private var currentData : String = '';

        protected function application1_creationCompleteHandler(event:FlexEvent):void
        {
            httpS.url = "data.xml";
            httpS.addEventListener(ResultEvent.RESULT, resultHTTP);
            httpS.resultFormat="e4x";
            httpS.send();

            timer.start();
            timer.addEventListener(TimerEvent.TIMER, updateXMLC);
        }

        private function updateXMLC(event:TimerEvent):void
        {
            xmlC.source = xmlDataList;
            if( !serviceRunning ){  // don't call for more data until 
                httpS.send();       // you've gotten back last call
            }
        }

        private function resultHTTP(event:ResultEvent):void
        {
            // make sure we have differences before rebinding
            var newData : String = event.result as String;
            serviceRunning = false;
            counter.text = Number(parseInt(counter.text,10)+1).toString();
            if( newData !=  currentData ){
                xmlData = event.result as XML;
                currentData = newData;
                xmlDataList = xmlData.dg.rows.row;
            }
        }
    ]]>
</mx:Script>
<mx:Label id="counter" text="0" horizontalCenter="0" verticalCenter="0" fontSize="72"/>
 </mx:Application>

If I understand your code correctly you are recovering your XML data from a file that's in the same folder as your SWF. If this is the case then what is most likely happening is that the XML file is being cached in your browser and it's returning the same cached version over and over again.

When dealing with these issues you can add a dummy URL variable that makes the browser think that the file you are retrieving is new and it forces it to recover the file again.

You can achieve this by doing something like this:

httpS.url = "data.xml?random="+Math.floor(Math.random()*1000);

If this doesn't fix the problem then maybe it can be associated to not resetting the variables that hold the actual XML data. You can accomplish this by resetting with new instances of your variables:

xmlData = new XML();
xmlDataList = new XMLList();
xmlC = new XMLListCollection();

Or if what you want is to just reset the XMLListCollection you can try using the "refresh()" method that is included in all Lists in Flex.

Important note: If you are using data binding with any of these variables this can cause Flex to not garbage collect the data because it is being used somewhere.

How long does it take to request, receive and process the XML? It sounds like it's a fairly big file, so if it's more than a second, you'll have multiple calls to the HTTPService happening concurrently (as I understand it, your current timer will try and fire off a request every second, regardless of whether the last one has returned).

I'm not sure if this is what you want, but I doubt it. If I'm right, and you only want to send a request after you've got the response from the last one, then you would probably be better off getting rid of the timer and its function, then changing the resultHTTP method to something like this:

private function resultHTTP(event:ResultEvent):void
{
    xmlData = event.result as XML;
    xmlC.source = xmlData.dg.rows.row;
    httpS.send();
}

Of course, you may want to put a delay on it starting each time:

    protected function application1_creationCompleteHandler(event:FlexEvent):void
    {
        httpS.url = "data.xml";
        httpS.addEventListener(ResultEvent.RESULT, resultHTTP);
        httpS.resultFormat="e4x";

        timer.start();
        timer.addEventListener(TimerEvent.TIMER, updateXMLC);
    }

    private function updateXMLC(event:TimerEvent):void
    {
        timer.stop();
        httpS.send();
    }

    private function resultHTTP(event:ResultEvent):void
    {
        xmlData = event.result as XML;
        xmlC.source = xmlData.dg.rows.row;
        timer.start();
    }

Sorry if I've misunderstood the issue though...

Either way, you probably want to put some sort of faultHTTP function in as well, so it also restarts the timer (or does whatever you need to do) on HTTP errors.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top