AS3 - 垃圾收集器什么时候运行?

时间:2010-08-30 23:52:53

标签: actionscript-3 flash garbage-collection

如果这是一个骗局,请道歉;我找不到它。

我已经阅读并了解了关于AS3垃圾收集器的grant skinner博客 - http://www.adobe.ca/devnet/flashplayer/articles/garbage_collection.html, 但我的问题不在那里。

这是我的问题。

假设我写了一些AS3代码,如:

statementOne;
statementTwo;

垃圾收集器是否有可能在我的两个语句中或之间运行,或者它只在我的“用户”代码完成并将控制权返回到闪存后运行?

我们有一个有时很慢的A-Star代码块, 我想消除GC作为潜在的罪魁祸首。 代码块显然比上面的例子复杂, 但它不涉及任何事件或其他异步的东西。

TIA, 猎户

6 个答案:

答案 0 :(得分:6)

垃圾收集器没有线程,所以一般来说,在代码运行时它不会运行。然而,有一个例外情况并非如此。

new关键字将在内存不足时调用垃圾收集器。

您可以运行此代码来观察此行为:

var vec:Vector.<*> = new Vector.<*>(9001);
for (;;) {
    vec[0] = new Vector.<*>(9001);
    vec = vec[0];
}

内存使用率会迅速上升到最大值(对我来说是1GB)然后一直持续到时间中断。

答案 1 :(得分:5)

这里有很多好的答案,但我认为一些细微之处尚未得到解决。

  1. Flash播放器实现两种垃圾收集。一个是引用计数,其中Flash保持对每个对象的传入引用的计数,并且知道当计数达到零时,可以释放对象。第二种方法是标记扫描,其中Flash偶尔会搜索并删除隔离的对象组(其中对象相互引用,但整个组没有传入引用)。
  2. 作为规范问题任何一种垃圾收集都可能随时发生。播放器不保证何时释放对象,并且标记扫描甚至可以分散在几个帧上。此外,无法保证GC的时间在Flash播放器版本或平台甚至浏览器之间保持不变。因此,在某种程度上,了解有关如何触发GC的信息并不是非常有用,因为您无法知道您的知识有多广泛适用。
  3. 除了上一点,一般来说,标记扫描很少发生,只在某些情况下触发。我知道某些播放器/操作系统配置,其中标记/扫描可以每N秒触发一次,或者当播放器超过总可用内存的P%时,但这是很久以前的事情,细节将会改变。相反,参考计数GC可能经常发生 - 至少在帧之间。我不认为我看到它们被证明更频繁地发生,但这并不意味着它不会发生(至少在某些情况下)。
  4. 最后,关于您的具体问题:根据我的经验,GC不太可能与您的性能问题有关。在循环期间可能会发生引用计数的GC处理,但通常这是一件好事 - 这种集合非常快,并且可以保持内存使用的可管理性。仔细管理您的参考文献的重点是确保这种GC及时发生。

    另一方面,如果在循环期间发生标记/扫描,那肯定会影响您的表现。但这是相对难以辨认和易于测试的。如果您正在PC上进行测试,并且您的应用程序没有经常爬上数百MB的内存使用量(使用System.totalMemory进行测试),那么您可能根本没有获得任何标记/扫描。如果你有它们,应该很容易验证相关的暂停是否很大,不经常,并且总是伴随着内存使用量的大幅下降。如果这些事情不成立,你可以忽视GC是你问题的一部分。

答案 2 :(得分:1)

据我所知,这没有记录。我感觉GC在执行代码时不会运行(也就是说,当你的代码在执行堆栈上时;每个框架,播放器为使用代码创建一个新的堆栈)。显然,这来自观察和我自己的Flash经验,所以我不会说这是100%准确。希望这是一个有根据的猜测。

这是一个简单的测试,似乎表明上述情况属实:

package {
    import flash.display.Sprite;
    import flash.net.FileReference;
    import flash.system.System;
    import flash.utils.Dictionary;
    import flash.utils.setTimeout;

    public class test extends Sprite
    {

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

        public function test()
        {
            testGC();
            setTimeout(function():void { 
                traceCount();
            },2000);            
        }

        private function testGC():void {
            var fileRef:FileReference;
            for(var i:int = 0; i < 100; i++) {
                fileRef = new FileReference();
                _dict[fileRef] = true;
                traceCount();
                System.gc();
            }
        }

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

    }
}

当涉及FileReference个对象时,GC似乎特别贪婪(再次,根据我的经验;据我所知,这没有记录。)

现在,如果你运行上面的代码,甚至显式调用System.gc(),当你的函数在堆栈上时,不会收集这些对象:你可以看到它们还活着看着字典的数量(由于显而易见的原因,它被设置为使用弱引用。)

当再次跟踪此计数时,在不同的执行堆栈中(由对setTimeout的异步调用引起),所有对象都已被释放。

所以,我认为GC似乎并不是你案件表现不佳的罪魁祸首。同样,这仅仅是观察,并且GC在此测试中执行用户代码时未运行的事实并不意味着它永远不会。可能,它不会,但由于没有记录,没有办法确定,我害怕。我希望这有帮助。

答案 3 :(得分:1)

GC不会像您在示例中那样在同一堆栈/同一帧上的两个语句之间运行。在执行下一帧之前,将释放内存。这就是大多数现代VM环境的工作方式。

答案 4 :(得分:0)

您无法确定GC何时运行。如果你试图阻止某些事情被GC进行,请在某处保留对它的引用。

答案 5 :(得分:0)

根据这个Tom from Gabob,足够大的孤立层次结构不会被垃圾收集。