Why Sytem.totalMemory keeps increasing?

2019-03-27 16:53发布

问题:

I have this blank project, just to check out System.totalMemory variable. As far as I can see, i get this values:

3076
3092
3096
3088
3092
3096
3100
3104
3108
3112
3117
3121
3125
3129
3133
3137
3141
3145
3149
...
And so on

I had no Flash open, no Internet Browser, no other instance of flash.

The project is blank, only one static text, and one dynamic text, called 'memory'. One *.as file, that contains this code:

package{
    import flash.display.Sprite;
    import flash.events.Event;
    import flash.system.System;
    import flash.text.TextField;
    public class Test extends Sprite {
        public function Test() {
            this.addEventListener(Event.ENTER_FRAME,Loop);
        }
        public function Loop(e:Event) {
            memory.text = String(System.totalMemory);
        }
    }
}

These are my Publish Settings.

I tested in the Debug and Published *.swf, same results.

I have no clue on this one, so please help.

回答1:

I think you've got a couple of things wrong.

First, your traces display totalMemory truncating the last 3 digits (since you're not doing it in the code, I assume it's because of the TextField width). It grows like this: 3076, 3092, 3096, etc. These are (roughly) Kilobytes, not bytes. Then you comment: "totalMemory after 2 hours: 3887104. My god". Now, if by 3,887,104 you mean 3,887,104 Kb, that would be about 3.8 Gb. I doubt that's the case, so let's assume you mean 3,887,104 bytes. That's about 3,800 Kb or 3.8 Mb. Not that much memory, actually, and more importantly, not so far from your initial 3,076 Kb.

I think this actually mislead another poster to think the player was incrementing memory usage by 4 bytes, when it actually increments by 4,096 bytes, or 4 Kb.

Second, even though the code is very simple, it does consume memory. For starters, each time the ENTER_FRAME event is dispatched, an Event object, which in turn contains references to other objects, strings, etc, is created. That takes memory. Then you are converting a number to a string implicitly (by printing totalMemory). That takes memory, too, whether you make an explicit conversion or not (the same applies if you make a trace instead of using a text field). On top of that, there's for sure other stuff going on that is not evident from an "actionscript point of view".

Now, I think part of the problem is that you're just tracing the current totalMemory. Looking at it, it seems as though it's growing, slowly but steadily, all the time. And that is true, but you're probably missing is that, at a slower pace, the GC kicks in and releases a lot of the memory that has been accumulating.

This is more evident if you modify the code to calculate a few things.

package{
    import flash.display.Sprite;
    import flash.events.Event;
    import flash.system.System;
    import flash.text.TextField;
    import flash.utils.getTimer;
    import flash.text.TextField;
    import flash.text.TextFormat;

    public class Test extends Sprite {

        private var peak:int            = 0;
        private var prev:int            = 0;
        private var cur:int             = 0;
        private var diff:int            = 0;
        private var decreaseCount:int   = 0;
        private var increaseCount:int   = 0;
        private var accumIncrease:int   = 0;
        private var accumDecrease:int   = 0;
        private var maxIncrease:int     = 0;
        private var maxDecrease:int     = 0;
        private var initTime:Number     = 0;
        private var elapsed:Number      = 0;

        private var time:TextField;
        private var info:TextField;

        public function Test() {
            initTime = getTimer();
            var tf:TextFormat = new TextFormat("Courier New",12);
            time = new TextField();
            time.defaultTextFormat = tf;
            time.width  = 250;
            addChild(time);
            info = new TextField();
            info.defaultTextFormat = tf;
            info.y = 15;
            info.width  = 250;
            info.height = 250;
            addChild(info);
            addEventListener(Event.ENTER_FRAME,Loop);
        }

        public function Loop(e:Event) {
            cur = System.totalMemory >> 12;

            elapsed     = (getTimer() - initTime) / 1000; 
            time.text   = "time running:        " + elapsed;

            if(cur == prev) {
                return;
            }

            if(cur > peak) {
                peak = cur;
            }

            if(cur > prev && prev > 0) {
                diff = cur - prev;
                if(diff > maxIncrease) {
                    maxIncrease = diff;
                }
                accumIncrease += diff;
                increaseCount++;
            } else if(cur < prev) {
                diff = prev - cur;
                if(diff > maxDecrease) {
                    maxDecrease = diff;
                }
                accumDecrease += diff;
                diff = -diff;
                decreaseCount++;
            }

            info.text   =   "current:           " + cur + "\n"
                        +   "previous:          " + prev + "\n"
                        +   "diff:              " + diff + "\n"
                        +   "peak:              " + peak + "\n"
                        +   "increaseCount:     " + increaseCount + "\n"
                        +   "decreaseCount:     " + decreaseCount + "\n"
                        +   "accumIncrease:     " + accumIncrease + "\n"
                        +   "accumDecrease:     " + accumDecrease + "\n"
                        +   "maxIncrease:       " + maxIncrease + "\n"
                        +   "maxDecrease:       " + maxDecrease;

            prev    = cur;

        }
    }
}

I'm using chunks of 4096 bytes as the unit (That's why I'm doing System.totalMemory >> 12. Just a fancy way to say System.totalMemory / 4096). I think it's more manageable and anyway totalMemory always return multiples of 4096 byes or 4kb. You can read more about Flash's GC here: https://developer.mozilla.org/en/MMgc. That part of the player is open source, and you can even read the sources if you're so inclined.

A brief explanation on what the code traces:

  • time running: Seconds elapsed since the swf began running
  • current: The amount of memory returned by System.totalMemory, in chunks of 4 Kb
  • previous: The previous value of totalMemory
  • diff: The difference between current and previous. Could be negative. This shows you if the memory usage increased or decreased with respect to the previous value.
  • peak: Self explaining. This is not very important.
  • increaseCount: The number of times current was greater that previous. Basically, it tells you how many times the totalMemory was increased, at least 1 chunk.
  • decreaseCount: The number of times previous was greater than current. This will tell you how many times memory has been released.
  • accumIncrease: The accumulated value of positive diff's. Will let you know how many chunks have been allocated.
  • accumDecrease: The accumulated value of negative diff's. Will let you know how many chunks have been released.
  • maxIncrease: The maximum number of chunks allocated within two loop executions.
  • maxDecrease: The maximum number of chunks released within two loop executions.

Now, let's look at some "snapshots" taken using this code.

This is a early snapshot, taken when the swf's been running for 3 seconds. Just note that current reads 760.

  • time running: 3 sec
  • current: 760
  • previous: 759
  • diff: 1
  • peak: 760
  • increaseCount: 3
  • decreaseCount: 0
  • accumIncrease: 6
  • accumDecrease: 0
  • maxIncrease: 3
  • maxDecrease: 0

After about 10 minutes:

  • time running: 574 sec
  • current: 763
  • previous: 762
  • curDiff: 1
  • peak: 834
  • increaseCount: 127
  • decreaseCount: 3
  • accumIncrease: 132
  • accumDecrease: 123
  • maxIncrease: 3
  • maxDecrease: 72

A couple of things to note:

  1. After about 10 minutes, current is very close to what it was at 3 sec: 763 vs 760. That means right now, the totalMemory is 3.052 Mb; At 3 sec, it was 3,040 Mb.
  2. The increase count is high, and the decrease count is low. That means the player has allocated memory a lot of times but released it very sparingly.
  3. maxIncrease is low and maxDecrease is high. Add that to 2) and you have an intersting pattern: The player allocates a small number of chunks frequently. It releases them at a much slower pace; when it does, though, it releases a big number of chunks.
  4. accumIncrease and accumDecrease are very close too.

Now, let the swf run some more time. After running for 50 minutes, the snapshot looks like this:

  • time running: 2989 sec
  • current: 931
  • previous: 930
  • diff: 1
  • peak: 931
  • increaseCount: 690
  • decreaseCount: 8
  • accumIncrease: 699
  • accumDecrease: 522
  • maxIncrease: 3
  • maxDecrease: 163

At this point you might think there's a leak. Note how the current memory is 931, versus the initial 760.

But look what happens at 3124 sec, ~52 minutes:

  • time running: 3142 sec
  • current: 767
  • previous: 768
  • diff: -1
  • peak: 962
  • increaseCount: 720
  • decreaseCount: 10
  • accumIncrease: 730
  • accumDecrease: 717
  • maxIncrease: 3
  • maxDecrease: 194

Before the GC kicked in, the peak grew to 962. But after that, current went down to 767, again, very close to the initial 760.

So, to wrap it up, the fact that memory usage grows up does not necessarily mean there's a leak. You just have to deal with the fact that the player is garbage collected, and that process is non deterministic. Memory will eventualy be reclaimed at some point (unless you do have a leak in your code, of course). You cannot determine when will this happen. It will happen when the player determines it's necessary. And in general, the player knows better.

That said, I think it's important to pay attention to possible leaks in your code. But just tracing System.totalMemory is not going to help you determine that. If you can, use a tool such as Flex Builder's memory profiler, which is not perfect but gives you much more useful info. And be careful when adding listeners to the stage and when using timers, the biggest culprits of memory leaks in the flash player.



回答2:

You may want to check to see which version of the Flash player you are running. If you have the IDE installed there is a decent chance you are running in the debug version of the player - which tends to not free up memory nearly as often (if at all) compared to the normal player.

If you are running in the debug player you can force a gc sweep - System.gc();



回答3:

As far as I can tell, on each loop, the call to System.totalMemory returns a uint representing the amount of memory in bytes current in use by Flash. The uint returned by the call to System.totalMemory is stored in memory (i.e., your RAM, not the textField that you have labeled "memory"). The memory.text reference is then updated to point to the space in memory occupied by the uint. The old uint is still in memory, but there's no reference to it. Uints are 32 bits (4 bytes) of data, so you are seeing your memory footprint increase by 4 bytes every time. Once the garbage collector runs, the space in memory occupied by the now derefrenced uints should get freed and you should see the memory drop.

However, when I run your sample code, I get nothing like that. It goes up and down, it doesn't go consistently up like yours, but I'm on OS X so it's a different player.



回答4:

As the text in the textfield changes, Flash will have to load different characters in to memory to display them. This could explain the few hundred bytes of increase you are getting.

Left alone, how much bigger does the footprint get?

UPDATE: Having thought about it, I think it is to do with the temporary bitmap graphics which are being created for Flash to render text... although you wouldn't expect that with trace... although trace must do some internal things too...

Have you thought of running it in FlexBuilder? That has a profiling tool which might tell you where your memory is going.



回答5:

Maybe this is obvious, but remember that System.totalMemory gives you the amount of memory used by Flash Player (static). If you have other Flash application opened, this might explain why your memory is increasing.