views:

60

answers:

2
1) var x1:X = new X();
2) var x2:X = new X();
...
3) x1.z = new SWFLoader(...);
...
4) x2.z = x1.z;
5) x1.z = null
6) x1 = null;

The last statement is useless because statement 4 guarantees that x1 and anything else it contains will never ever be garbage collected as long as x2.z exists. Does anyone else think this is bizarre? This was a major shock and drawback to me for something that I needed to do. Is there any workaround at all?

The only reason this would make sense is if everything in x1 was stored in contiguous memory or something, but when you say "x1.z = new ..." in most languages that implies its going and allocating a new block of memory somewhere else and returning a pointer to it (a pointer that is subsequently assigned to x2.z as well.) I was always interpreting "reference" in actionscript as "pointer".

Of course some might say, well you could still delete everything in x1 individually. But if not for statement 4 above, statement 6 would mark everything in x1 for deletion.

(Note: the only reason I put statement 5 in was to tell flash player "I really don't care about x1.z anymore", but it made no difference.)

+1  A: 

If I understood your question correctly, you are saying that the following code should leak x1. But it doesn't. How are you testing that x1 is not eligible for collection? Can you post some working code that reproduces the problem?

package {
    import flash.display.MovieClip;
    import flash.display.Sprite;
    import flash.net.URLLoader;
    import flash.system.System;
    import flash.utils.Dictionary;
    import flash.utils.setInterval;

    public class test extends Sprite
    {

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

        private var x1:Foo;
        private var x2:Foo;

        public function test()
        {
            testGC();
            setInterval(function():void { 
                traceCount();
            },100);         
        }

        private function testGC():void {
            x1 = new Foo();
            x2 = new Foo();

            _dict[x1] = true;
            _dict[x2] = true;

            x1.z = new URLLoader();
            x2.z = x1.z;

            x1 = null;


        }

        private function traceCount():void {
            var count:int = 0;
            for each(var i:* in _dict) {
                count++;
            }
            trace(count);
            System.gc();
        }

    }
}

import flash.net.URLLoader;

class Foo {

    public var z:URLLoader;
}
Juan Pablo Califano
sure I'll post some code just give me a few minutes - just now seeing this.
Mark
It may take me a bit to post some code up here, as I already backtracked a lot of it and took a different direction. But in my case x1 and x2 were instances of a statically defined class that did not extend any existing class. I notice in your example x1 and x2 are instances of the flash defined MovieClip. So there is difference right there. Do I just have to extend the right class to solve my problem. Also, in my case z would be a declared member of the class X; I am actually not familiar with dynamically adding a property z to the class MoveClip as you have done.
Mark
Or would dynamically adding the property z as you did have solved my problem.
Mark
@Mark. I don't think so. I used a movieclip just because it was the first dynamic class that came to mind when writting the test (and wanted to use a dynamic class to keep it short and simple). But this shouldn't change how GC works. In fact, I just changed the posted code to use a class defined by me and it doesn't leak the nulled out instance. So maybe there's some other thing going on in your code. Also, it's not clear to me how you established that you have a leak. Could it be that you're seeing a "false positive", so to speak?
Juan Pablo Califano
Juan - I just reverted my original code and its still doing it - I know I came up with a new test scenario above that didn't indicate it, but my original code still is. THere is simply no other explanation for what is going on other than the theory I elaborated on in the OP above. I instantiate a series of objects and Windows Task Manager shows no increase in memory usage. I add one single line of code: img_bg_prev = rad_xf_prev.img_bg; and there starts to be a 7 megabyte memory leak in Task manager with each new object. In the above line, (cont)
Mark
that line of code is actually being run from within a method of the class in question. rad_xf_prev is a member of Rad_XF and rad_xf_prev is itself a Rad_XF. So that one added line of code assigns a member of rad_xf_prev to another member of the calling Rad_XF (and then immediately sets rad_xf_prev to null as always) and that one additional line results in in a 7 mg memory leak with each new object. No explanation from me currently - don't know if you'll come up with anything either, but I already jettisoned all this code anyway.
Mark
A: 

I just tried to duplicate this with new code and was unable to, so I'm at a loss. The original code is reverted now and the class was much larger than the test scenario I just came up with. Guess I better close this. Here is the test scenario I just ran, FWIW:

package my_components 
{

  import mx.controls.SWFLoader;

  public class Test {

    public var arr:Array = new Array();

    public var z:SWFLoader;

    public function Test() {
      for (var n:int=0; n<1000000; n++)
        arr.push(n);
    }  

  }

}       

private var x1:Test;
private var x2:Test;

private function InitWR(fnCFG:String = null):void {

  try { 
    new LocalConnection().connect('foo'); 
    new LocalConnection().connect('foo'); 
  } catch (e:*) {} 

  Dumper.info(System.totalMemory.toString());   

  x1 = new Test();
  x1.z = new SWFLoader();

  x2 = new Test();

  x2.z = x1.z;

  x1 = null;

  try { 
    new LocalConnection().connect('foo'); 
    new LocalConnection().connect('foo'); 
  } catch (e:*) {} 

  Dumper.info(System.totalMemory.toString());   

  Dumper.info("---------------");   
Mark
I'll unarchive the original code tomorrow and see what the deal was. I was using the Window Task Manager to identify a memory leak at one point.
Mark
@Mark. A couple of notes. System.gc is the preferred way to force gc (only works in debug mode). The LocalConnection trick was a hack when System.gc didn't exist. Since it's undocumented it could change without warning (but you shouldn't be forcing gc unless you're trying to diagnose a problem, so no big deal anyway). The task manager is not a good tool to find a leak. It merely reports the memory used by the player. The player could be allocating memory for thing other than you Actionscript code, so don't rely on it. Just use it a *very* rough guide if you don't have a better tool at hand.
Juan Pablo Califano
(cont) System.totalMemory is not good either. At best it could *hint* you there's a leak, but it won't help you determining where it is (if there is a leak at all). A more reliable trick is using a weak-referenced dictionary as in the sample code I posted. Or better yet, use a memory profiler, such as the one that comes with FlashBuilder, if that's available to you.
Juan Pablo Califano