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:
- 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.
- 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.
- 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.
- 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.