AS3 - 垃圾回收器何时运行?

6

如果这是一个重复的问题,我很抱歉;我找不到它。

我已经阅读并理解了Grant Skinner关于AS3垃圾回收器的博客 - http://www.adobe.ca/devnet/flashplayer/articles/garbage_collection.html, 但我的问题在那里没有涉及。

这是我的问题:

假设我编写了一些AS3代码:

statementOne;
statementTwo;

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

我们有一个A-Star代码块,有时会很慢,我想排除GC作为潜在的罪犯。 该代码块显然比上面的示例更复杂,但不涉及任何事件或其他异步内容。

谢谢, Orion


亲爱的Orion,GC不能帮你解决内存泄漏问题,所以我建议使用一些分析工具,例如Flash Builder Profiler,将你的慢代码块重写为更快的代码块,因为你的应用程序将有不同的用户,并且他们应该被通知最小化应用程序对硬件内存的使用。 - Eugene
谢谢你的评论,尤金。 我的问题并不是关于内存泄漏的, 而是关于试图消除垃圾回收作为我遇到的特定性能问题的潜在原因。 - orion elenzil
6个回答

6

垃圾收集器没有多线程,所以一般来说它不会在您的代码运行时运行。然而,有一个例外情况是不成立的。

new 关键字将在内存用尽时调用垃圾回收器。

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

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

内存使用率会迅速上升到最大值(对我来说是1GB),然后保持不变,直到时间截止。


+1。这是一个比我想出来的更好的测试。我没有想到可以 brute forcing new - Juan Pablo Califano

5
这里有很多好的答案,但我认为还有一些微妙之处没有被解决。
Flash播放器实现了两种垃圾回收方式。一种是引用计数,其中Flash保留每个对象的传入引用计数,并知道当计数达到零时可以释放该对象。第二种方法是标记清除,其中Flash偶尔搜索并删除孤立的对象组(其中对象相互引用,但整个组没有传入引用)。
作为规范的一部分,任何一种垃圾回收都可能在任何时间发生。播放器不保证对象何时被释放,标记清除甚至可能分散在几帧中进行。此外,不能保证GC的定时在Flash播放器版本、平台或甚至浏览器之间保持相同。因此,在某种程度上,了解GC如何触发并不是非常有用,因为您无法知道您的知识有多广泛适用。
除了前面的观点,一般来说,标记清除不经常发生,只在特定情况下触发。我知道某些播放器/操作系统配置会在每N秒或每当播放器超过总可用内存的P%时触发标记/清除,但这是很久以前的事情,细节已经改变了。相比之下,引用计数GC可能经常发生-至少在帧之间。我认为我没有看到它们演示更频繁地发生,但这并不意味着它不会发生(至少在某些情况下)。
最后,关于您的特定问题:根据我的经验,GC与您的性能问题几乎没有任何关系。引用计数GC处理可能会在循环期间发生,但通常这是一件好事-这种收集非常快速,并且可以使您的内存使用量可控。精心管理引用的整个目的是确保这种GC及时发生。
另一方面,如果标记/清除在循环期间发生,那肯定会影响您的性能。但这相对较难错过并易于测试。如果您正在PC上进行测试,并且应用程序未经常爬升到数百MB的内存使用量(使用System.totalMemory进行测试),则您可能根本没有进行标记/清除。如果您确实有它们,那么验证相关暂停是否大、不经常和始终伴随着内存使用量的大幅下降就很容易了。如果这些事情不成立,则可以忽略GC是您问题的一部分的想法。

呵呵,如果在回答之前我点击了提问者的链接,我会发现很多内容已经涵盖了。希望最后两段仍然有所帮助。 - fenomas
非常好,感谢您周到的回复。总的来说,看起来GC不太可能是我遇到的偶发性性能问题的一部分,这是非常好的!我可以修复我的代码,但我无法修复GC。非常感谢您的回复。 - orion elenzil

1
据我所知,这并没有被记录下来。我有一种感觉,在执行您的代码时(也就是说,在您的代码在执行堆栈上时),垃圾回收器不会运行;每个帧,播放器都会为使用代码创建一个新的堆栈。显然,这是基于观察和我自己在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 对象时,垃圾回收器似乎特别贪婪(再次强调,这是我的经验;据我所知,这并没有被记录在文档中)。

现在,即使显式调用 System.gc(),在函数处于堆栈上时,对象也不会被清除:您可以看到它们仍然存活,观察字典的计数(出于明显的原因,字典设置为使用弱引用)。

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

所以,我想说的是,垃圾回收器似乎并不是您的代码性能不佳的罪魁祸首。同样地,这只是一种简单的观察,并且 GC 在此测试中未在执行用户代码时运行并不意味着它永远不会运行。很可能不会运行,但由于这并没有记录在文档中,所以无法确定,我恐怕无能为力。希望这能有所帮助。


太棒了,感谢您的调查。我认为你是正确的。 - orion elenzil
另外,就我所知并经过实验证实,System.gc()仅仅是请求系统进行一次垃圾回收,而在我的测试中,它似乎通常被忽略了。 - orion elenzil
@orion elenzil。根据我的经验,垃圾回收器几乎总是在运行(无论我是从代码还是从FlexBuilder分析器中调用它)。然而,也有一些注意事项。你必须使用带有调试版本播放器的swf文件进行播放,有时似乎需要调用两次才能使其运行(想象一下!)。此外,它很可能会延迟运行(可能是在用户代码执行之后)。 - Juan Pablo Califano
有趣。我可能误解了我所看到的内容。我确实使用了最近的调试播放器,并调用了两次GC。 - orion elenzil

1

GC(垃圾回收)不会在同一堆栈/框架上运行两个语句,就像您在示例中所示的那样。在执行下一个框架之前,将释放内存。这是大多数现代虚拟机环境的工作方式。


不,实际上我是在说现代虚拟机中的垃圾回收不会花费处理时间来执行不必要的释放语句。在我看来,Flash、iPhone、Java和.NET都是现代的。它们会等待线程休眠、堆栈完成或运行循环结束。 - tidwall
@TandemAdam 当然。我指的是内存池,而不是垃圾回收。 - tidwall

0
根据来自Gabob的Tom的说法,足够大的孤立层次结构不会被垃圾回收。

0

你无法确定垃圾回收(GC)何时运行。如果你想阻止某个对象被 GC 回收,请在某个地方保留对它的引用。


嗨,谢谢回复。我知道在整个程序运行过程中无法确定垃圾回收器何时运行,但我的问题是,我能否知道它不会在执行" x += 1 "的过程中运行。 - orion elenzil
1
你有Flex Builder吗?它有一个内存分析器,可能可以回答你的一些问题。 - ThatSteveGuy

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接