为什么Sytem.totalMemory不断增加?

Jor*_*jon 7 memory flash actionscript actionscript-3

我有这个空白项目,只是为了检查System.totalMemory变量.据我所知,我得到这个值:

3076
3092
3096
3088
3092
3096
3100
3104
3108
3112
3117
3121
3125
3129
3133
3137
3141
3145
3149
...
And so on
Run Code Online (Sandbox Code Playgroud)

我没有打开Flash,没有Internet浏览器,没有其他闪存实例.

该项目为空白,只有一个静态文本和一个动态文本,称为"内存".一个*.as文件,包含以下代码:

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);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

这些是我的发布设置.

http://i.stack.imgur.com/3k1vq.png http://i.stack.imgur.com/rwM1D.png

我在Debug和Published*.swf中测试了相同的结果.

我对此没有任何线索,所以请帮忙.

Jua*_*ano 25

我觉得你有一些错误.

首先,您的跟踪显示totalMemory截断最后3位数字(因为您没有在代码中执行此操作,我认为这是因为TextField宽度).它像这样增长:3076,3092,3096等.这些是(大致)千字节,而不是字节.然后你评论:"2小时后的总记忆:3887104.我的上帝".现在,如果用3,887,104表示3,887,104 Kb,则约为3.8 Gb.我怀疑是这种情况,所以我们假设你的意思是3,887,104字节.那大概是3,800 Kb或3.8 Mb.实际上并没有那么多记忆,更重要的是,距你最初的3,076 Kb还不远.

我认为这实际上误导了另一张海报,认为玩家将内存使用量增加了4个字节,实际上它增加了4,096字节或4 Kb.

其次,即使代码非常简单,它也会消耗内存.对于初学者,每次调度ENTER_FRAME事件时,都会创建一个Event对象,该对象又包含对其他对象,字符串等的引用.这需要记忆.然后,您将隐式地将数字转换为字符串(通过打印totalMemory).无论你是否进行显式转换,这都会占用内存(如果你创建一个跟踪而不是使用文本字段,则同样适用).最重要的是,从"动作"的角度来看,肯定还有其他的东西是不明显的.

现在,我认为问题的一部分是你只是追踪当前的totalMemory.看着它,它似乎一直在缓慢但稳定地增长.这是事实,但你可能会错过的是,以较慢的速度,GC开始并释放大量已经积累的内存.

如果您修改代码以计算一些内容,则更明显.

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;

        }
    }
}
Run Code Online (Sandbox Code Playgroud)

我使用4096字节的块作为单位(这就是为什么我在做System.totalMemory >> 12.只是一种奇特的方式来说System.totalMemory/4096).我认为它更易于管理,无论如何,totalMemory总是返回4096 byes或4kb的倍数.您可以在此处阅读有关Flash GC的更多信息:https://developer.mozilla.org/en/MMgc.播放器的那部分是开源的,如果你这么倾向,你甚至可以阅读这些资源.

关于代码跟踪的简要说明:

  • 时间运行: 自swf开始运行以来已经过了几秒钟
  • current: System.totalMemory返回的内存量,以4 Kb为单位
  • previous: totalMemory的前一个值
  • diff: 当前和之前的差异.可能是消极的.这将显示内存使用量是否相对于先前值增加或减少.
  • 高峰: 自我解释.这不是很重要.
  • increaseCount: 当前值大于之前的次数.基本上,它告诉你totalMemory增加了多少次,至少一个块.
  • decreaseCount: previous之前的次数大于当前次数.这将告诉您已释放内存的次数.
  • accumIncrease: 正差异的累积值.会告诉你已经分配了多少块.
  • accumDecrease: 负差异的累积值.会告诉你已经释放了多少块.
  • maxIncrease: 两次循环执行中分配的最大块数.
  • maxDecrease: 两次循环执行中释放的最大块数.

现在,让我们看看使用此代码拍摄的一些"快照".

这是早期快照,当swf运行3秒钟时拍摄.请注意,目前读取760.

  • 时间跑:3秒
  • 目前:760
  • 上一篇:759
  • 差异:1
  • 高峰期:760
  • increaseCount:3
  • decreaseCount:0
  • accumIncrease:6
  • accumDecrease:0
  • maxIncrease:3
  • maxDecrease:0

大约10分钟后:

  • 时间跑:574秒
  • 目前:763
  • 上一篇:762
  • curDiff:1
  • 高峰期:834
  • increaseCount:127
  • 减少数量:3
  • accumIncrease:132
  • accumDecrease:123
  • maxIncrease:3
  • maxDecrease:72

有几点需要注意:

  1. 大约10分钟后,电流非常接近3秒时的电流:763比760.这意味着现在,总存储量为3.052 Mb; 在3秒时,它是3,040 Mb.
  2. 增加计数高,减少计数低.这意味着玩家已经分配了很多次内存但是非常谨慎地释放了它.
  3. maxIncrease低,maxDecrease高.将其添加到2)并且您有一个模式:玩家经常分配少量的块.它以更慢的速度释放它们; 但是,当它发生时,它释放了大量的块.
  4. accumIncrease和accumDecrease也非常接近.

现在,让瑞士法郎再跑一段时间.运行50分钟后,快照如下所示:

  • 时间跑:2989秒
  • 目前:931
  • 上一篇:930
  • 差异:1
  • 高峰期:931
  • increaseCount:690
  • 减少数量:8
  • accumIncrease:699
  • accumDecrease:522
  • maxIncrease:3
  • maxDecrease:163

此时你可能会认为有泄漏.注意当前内存是931,而不是最初的760.

但看看3124秒,52分钟发生的事情:

  • 时间:3142秒
  • 目前:767
  • 上一个:768
  • diff:-1
  • 高峰期:962
  • increaseCount:720
  • 减少数量:10
  • accumIncrease:730
  • accumDecrease:717
  • maxIncrease:3
  • maxDecrease:194

在GC开始之前,峰值增长到962.但在那之后,当前下降到767,再次非常接近最初的760.

因此,为了将其包装起来,内存使用量增长的事实并不一定意味着存在泄漏.你只需要处理玩家被垃圾收集的事实,而且这个过程是不确定的.记忆最终会在某个时候被回收(当然,除非你的代码确实存在泄漏).您无法确定何时会发生这种情况.当玩家确定有必要时会发生这种情况.一般来说,玩家会更清楚.

也就是说,我认为注意代码中可能存在的泄漏非常重要.但只是跟踪System.totalMemory不会帮助您确定.如果可以,请使用Flex Builder的内存分析器等工具,这不是完美的,但会为您提供更多有用的信息.在向舞台添加监听器和使用计时器时要小心,闪存播放器中内存泄漏的最大罪魁祸首.