代码之家  ›  专栏  ›  技术社区  ›  Jorjon

为什么sytem.totalmemory不断增加?

  •  7
  • Jorjon  · 技术社区  · 15 年前

    我有一个空白项目,只是为了检查system.totalmemory变量。据我所见,我得到以下值:

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

    我没有打开闪存,没有网络浏览器,没有其他闪存实例。

    项目为空,只有一个静态文本和一个称为“内存”的动态文本。一 *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);
            }
        }
    }
    

    这些是我的发布设置。

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

    我在调试中测试并发布了*.swf,同样的结果。

    我对这件事一无所知,请帮忙。

    5 回复  |  直到 9 年前
        1
  •  25
  •   Juan Pablo Califano    15 年前

    我想你出了点问题。

    首先,您的跟踪显示截断最后3位数字的总内存(因为您没有在代码中执行此操作,所以我认为这是因为textfield宽度)。它是这样增长的:3076、3092、3096等等。这些(大约)是千字节,而不是字节。然后你评论:“2小时后的总内存:3887104。我的上帝。现在,如果3887104的意思是3887104kb,大约是3.8GB。我怀疑是这样的,所以假设您的意思是3887104字节。大约3800 kb或3.8 MB。实际上,没有那么多内存,更重要的是,距离您最初的3076 KB也不远。

    我认为这实际上误导了另一个海报,认为玩家的内存使用量增加了4字节,而实际上增加了4096字节,即4KB。

    第二,即使代码非常简单,它也会消耗内存。对于初学者,每次发送enter_帧事件时,都会创建一个事件对象,该对象依次包含对其他对象、字符串等的引用。这需要记忆。然后将数字隐式转换为字符串(通过打印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;
    
            }
        }
    }
    

    我使用4096字节的块作为单位(这就是为什么我要使用System.TotalMemory>>12)。只不过是一种说system.totalmemory/4096的奇特方式)。我认为它更易于管理,无论如何,总内存总是返回4096 byes或4kb的倍数。您可以在此处阅读有关Flash GC的更多信息: https://developer.mozilla.org/en/MMgc . 玩家的那部分是开放源码的,如果你愿意的话,你甚至可以阅读源码。

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

    • 时间运行: 自SWF开始运行以来经过的秒数
    • 电流: System.TotalMemory返回的内存量,以4KB为单位。
    • 以前: totalmemory的前一个值
    • 微分: 当前和以前的差异。可能是否定的。这将显示相对于前一个值,内存使用是否增加或减少。
    • 峰: 自我解释。这并不重要。
    • 增加: 当前的次数大于以前的次数。基本上,它告诉你总内存增加了多少倍,至少1块。
    • 减少: 以前的次数大于当前的次数。这将告诉您释放了多少次内存。
    • 累加: 正diff的累积值将让您知道分配了多少块。
    • 累减: 负diff的累积值将让您知道释放了多少块。
    • 最大增长: 在两个循环执行中分配的最大块数。
    • 最大下降: 在两个循环执行中释放的最大块数。

    现在,让我们看看使用此代码拍摄的一些“快照”。

    这是一个早期快照,在SWF运行3秒钟后拍摄。请注意,电流读数为760。

    • 运行时间:3秒
    • 电流:760
    • 上一页:759
    • 差异:1
    • 峰值:760
    • 增加计数:3
    • 递减计数:0
    • 精度:6
    • 累计递减:0
    • 最大增量:3
    • 最大减少量:0

    大约10分钟后:

    • 运行时间:574秒
    • 电流:763
    • 上一个:762
    • 电流差:1
    • 峰值:834
    • 增加计数:127
    • 递减计数:3
    • 精度:132
    • 累计降幅:123
    • 最大增量:3
    • 最大减少量:72

    需要注意的几点:

    1. 大约10分钟后,电流为 非常接近3秒时的情况: 763比760。这意味着现在, 总内存为3.052 MB;3 秒,是3040兆。
    2. 增加计数很高, 减少计数低。那意味着 玩家已分配内存A 很多次,但释放了它非常 节省地。
    3. MaxIncrease低MaxDecrease 是高的。再加上2)你就有了 一个有趣的模式:玩家 分配少量块 经常。它在一个 速度要慢得多;但是,当速度慢得多时, 释放大量块。
    4. AccumIncrease和AccumDecrease是 非常接近。

    现在,让主权财富基金运作更多的时间。运行50分钟后,快照如下:

    • 运行时间:2989秒
    • 电流:931
    • 上一个:930
    • 差异:1
    • 峰值:931
    • 增加计数:690
    • 递减计数:8
    • 精度:699
    • 累计降幅:522
    • 最大增量:3
    • 最大减少量:163

    此时,你可能认为有泄漏。请注意当前内存是931,而初始内存是760。

    但是看看3124秒,大约52分钟后会发生什么:

    • 运行时间:3142秒
    • 电流:767
    • 上一个:768
    • 差异:-1
    • 峰值:962
    • 增加计数:720
    • 递减计数:10
    • 精度:730
    • 累计降幅:717
    • 最大增量:3
    • 最大减少量:194

    在GC开始之前,峰值增长到962。但在那之后,电流又降到了767,非常接近最初的760。

    所以,总结一下,内存使用量增长的事实并不一定意味着存在泄漏。您只需要处理这样一个事实:播放器是垃圾收集的,而这个过程是非确定性的。内存可能会在某个时刻被回收(当然,除非您的代码确实有漏洞)。你不能确定什么时候会发生这种情况。当玩家认为有必要时,就会发生这种情况。一般来说,玩家更清楚。

    也就是说,我认为重要的是要注意代码中可能存在的泄漏。但仅仅跟踪system.totalmemory并不能帮助您确定这一点。如果可以的话,可以使用一个工具,比如flex builder的memory profiler,虽然它并不完美,但是可以提供更多有用的信息。在舞台上添加听众和使用计时器时要小心,造成内存泄漏的最大罪魁祸首是Flash播放器。

        2
  •  1
  •   Branden Hall    15 年前

    您可能需要检查运行的Flash播放器的版本。如果安装了IDE,那么在播放器的调试版本中运行的可能性相当大——与正常播放器相比,这种情况下释放内存的频率(如果有的话)几乎没有平时那么高。

    如果在调试播放器中运行,则可以强制执行gc扫描-system.gc();

        3
  •  0
  •   jorelli    15 年前

    据我所知,在每个循环中,对system.totalmemory的调用返回一个uint,表示闪存当前使用的内存量(以字节为单位)。调用system.totalmemory返回的uint存储在内存中(即RAM,而不是标记为“memory”的文本字段)。然后更新memory.text引用以指向uint占用的内存空间。旧的uint仍在记忆中,但没有提到它。uint是32位(4字节)的数据,因此您可以看到内存占用每次增加4字节。一旦垃圾收集器运行,现在被取消绑定的uint所占用的内存空间将被释放,您将看到内存下降。

    但是,当我运行示例代码时,我什么也没有得到。它会上下波动,不像你的那样总是上下波动,但我在OSX上,所以它是一个不同的玩家。

        4
  •  0
  •   Iain    15 年前

    当文本字段中的文本更改时,flash必须将不同的字符加载到内存中以显示它们。这可以解释你得到的几百字节的增长。

    别说了,脚印要大多少?

    更新:经过考虑,我认为这是与临时位图图形,正在创建的闪存渲染文本…虽然你不希望有痕迹…尽管trace也必须做一些内部的事情…

    你想过在flexbuilder中运行它吗?它有一个分析工具,可以告诉您内存的去向。

        5
  •  0
  •   gabriel-laet    15 年前

    也许这很明显,但请记住System.TotalMemory提供了Flash播放器使用的内存量(静态)。如果您打开了其他闪存应用程序,这可能解释了为什么您的内存在增加。