全部应用程序都要管理内存。应用程序的内存管理包括用于肯定什么时候分配内存,分配多少内存,什么时候将内容放入回收站,以及什么时候清空回收站的准则。MMgc是 Flash Player用于几乎全部内存分配工做的通用内存管理器。理解MMgc如何管理内存是优化您的代码和您应用程序的性能的一个重要部分。
垃圾收集器自动回收的内存被视为“受管理的内存”。垃圾收集器肯定内存什么时候再也不被应用程序使用并回收它。本文分析Flash Player 11和AIR 3中的内存分配、垃圾收集流程和新的pauseForGCIfCollectionImminent()API。
内存分配
Flash Player使用一个页面分配程序(GCheap)来从OD分配大块(几MB)的内存。Gcheap而后将大内存块分解为较小的4K页面,并根据须要将这些页面提供给垃圾收集(GC)内存管理器。
图1. GCHeap从OS分配内存,将它分解为4K的页面,并将这些页面提供给GC。
GC而后使用这些4K页面为系统中不大于2K的对象提供内存。
图2. 4K页面由GC分配给小于2K的对象
对于大于2K的对象(位图、视频、文件等),GCHeap向一个大型的内存分配程序提供一组连续的4K内存块。
当一个大内存块中几乎全部4K页面都分配了时,Flash Player运行垃圾收集来回收未使用的内存,而后GCHeap尝试从OS分配更多内存。换句话说,垃圾收集仅由内存分配触发。这一事实很重要,在测试和 分析期间必定要记住,由于它意味着空闲应用程序的内存使用从不会改变。
堆和堆栈
堆是分配给在运行时建立或初始化的任何对象的内存。堆上的对象会一直存在到它们被垃圾收集。
图3. 对象A存在于堆上。它由堆栈上的局部变量o引用。(图字:堆栈内存 堆内存)
堆栈是存储在编译时定义的全部变量的内存。堆栈内存以一种顺序方式使用和重用。推送操做将一些内容添加到堆栈顶部。弹出操做从堆栈顶部删除一些内容。访问堆栈中间的内容的惟一方式是删除它上方的全部内容。
局部方法变量、参数和关于在一个方法完成时返回到何处的信息,在方法运行时被推送到堆栈上。对堆栈的更改发生得很是快。对象的堆栈引用可能很是短暂。这些对象引用可能存在于堆栈上,但分配给这些对象的内存来自堆。
图4. 局部变量在定义时被推送到堆栈上。关于在一个方法完成时返回何处的信息也推送到堆栈上。
Flash运行时垃圾收集实现
Flash Player和AIR结合使用延迟的引用计数和保守的标记并清除(mark-and-sweep)方法。
延迟的引用计数
在延迟的引用计数中,堆和堆栈引用之间存在区别。由于堆栈变化很快,并可能包含很是短暂的引用,因此引用计数不会在堆栈引用上执行。而在堆上为引用维护引用计数。
图5. 对象会跟踪它们拥有多少个引用。
堆上的每一个对象会跟踪指向它的信息数量。每次您建立一个对象的引用,该对象的引用计数就会递增。当您删除一个引用时,该对象的引用计数会递减。若是对象的 引用计数为0(没有任何信息指向它),它会被添加到零计数表(Zero Count Table,ZCT)中。当ZCT填满后,就会扫描堆栈以查找任何从堆栈到ZCT上的对象的引用。ZCT上任何没有堆栈引用的对象都会被删除。
延迟引用计数的一个问题是循环引用。若是ObjectA和ObjectB彼此引用,而系统中没有其余对象指向它们,它们将从不会拥有一个零引用计数,所以从不知足使用引用计数进行垃圾收集的资格。这时可使用“标记并清除”的垃圾收集方法。
图6. Object A和Object B彼此引用,但没有其余引用。
标记/清除
在Flash Player或AIR中运行的应用程序具备多个GCRoot。您能够将GCRoot视为一个树的一部分,它将应用程序的对象看成树枝。舞台是一个 GCRoot。加载程序是GCRoot。某些菜单是GCRoot。让在供应用程序使用的每一个对象可从应用程序内的一个GCRoot访问。GCRoot从不 会被垃圾收集。
应用程序中的每一个对象有一个“标记位”。当垃圾收集的标记阶段开始时,全部这些标记位会被清除。MMgc会跟踪应用程序中的全部GCRoot。垃圾收集器 首先从这些根开始,跟踪每一个对象并为它到达的每一个对象设置标记位。任何再也不可以从任何根到达的对象也再也不可以从应用程序的任何地方到达——它的标记位不会 在标记阶段设置。收集器完成对它找到的全部对象进行标记以后,就会开始清除阶段。任何没有设置标记位的对象都会被销毁,它的内存会被回收。
图7. 一个循环引用中的对象没有被标记。
图7显示,每一个可从Gcroot到达的对象都设置了本身的标记位(蓝色)。一个循环引用中的两个对象(ObjectA和ObjectB)不可从GCRoot到达。它们的标记位将不会设置。所以,即便它们没有零引用计数,这两个对象也会被垃圾收集。
弱引用
Flash Player也能够维持对某些类型的对象的“弱引用”。弱引用是一种对垃圾收集器的正常跟踪过程(跟随全部根来查找可到达的对象的过程)不可见的引用。
当您实例化一个新字典时,能够代表您但愿它与字典的键创建较弱的关联。
var dictionary = new Dictionary( true );
d[ someObject ] = someValue;
您也能够在添加事件监听器时,将addEventListener()的函数useWeakReference参数设置为true。
obj.addEventListener( "type", handler, false, 0, true );
在这两种状况下,您都会要求Flash Player在两个对象之间创建引用,但以一种较弱的方式保持该引用。具体来说,这意味着这个具体的引用在标记期间不会被跟随。
图8. 若引用在标记期间不会被跟踪。
在这种状况下,到Object B的惟一路径是弱的。在跟踪期间将不会通过它,所以Object B不会被标记,并会被收集。可是,若是还有另外一个到Object B的强路径,Object B将被标记并被持久化。
图9. 具备强引用的对象将在跟踪期间被找到并标记。
您应该始终清理未使用的引用,从字典删除未使用的项,以及使用removeEventListener()。可是,有时清理未使用的引用不切实际或没法作 到。好比在您的类在您不知情的状况下实例化和销毁时——项渲染器就是经过这种方式使用的。在这些状况下,维持对象的若引用将容许Flash Player最终删除它们并回收内存。
保守收集
MMgc被视为一种保守的标记/清除收集器。MMgc没法肯定内存中的某些值是对象指针(内存地址)仍是数字值。为了不意外地收集值可能指向的对 象,MMgc假设每一个值均可以是一个指针。所以,一些没有实际被指向的对象将从不被收集,将被视为一种内存泄漏。尽管您但愿最小化内存泄漏以优化性能,但 由保守的GC所致使的偶然泄漏多是随机的,不会随时间增加,而且对应用程序性能的影响比开发人员致使的泄漏小得多。
增量收集
不幸的是,垃圾收集可致使Flash Player在收集过程完成时按期暂停。这种暂停与应用程序当前运行的内存量成正比。它可能比但愿的时间更长,在一些程序中能够察觉到。
标记阶段是垃圾收集过程当中最消耗时间的部分。因为此事实,标记过程使用一个动做队列和一个3色算法增量化了。该队列在标记增量之间维护标记状态。
表1. 3色算法php
![]() |
黑色对象已标记,再也不位于队列中。 |
![]() |
灰色对象位于队列中,还未被标记。 |
![]() |
白色对象既未标记也不在队列中。 |
在标记阶段的开始,因此GCRoot被推送到队列中并变为灰色。
图10. GCRoot在推送到工做队列中时变成灰色。
随着标记过程的继续,标记的对象变为灰色,并从工做队列删除。
图11. 标记的对象是黑色的,再也不在工做队列中。
此过程会正常继续进行,直到将一个新对象(白色)添加到一个黑色对象上。当发生此状况时,白色对象从不会设置它的标记位,由于它们的GCRoot已标记。不设置它们的标记位,它们将在清除阶段被垃圾收集。
图12. 新对象被添加到之前标记的对象上。
要预防此问题,能够在MMgc中使用一个白色边界来强制将任何添加到黑色对象上的白色对象当即添加到工做队列中。
图13. 添加到之前标记的对象中的新对象被当即添加到工做队列中。
经过使用工做队列和3色算法,可开始和中止标记阶段来帮助避免长时间、意外的垃圾收集暂停。
迫近度
标记阶段多是垃圾收集中最耗时的部分,但实际上清空回收站(从新分配空闲内存)也比较耗时。从新分配还可能致使应用程序暂停。垃圾收集器离标记阶段的完成和清除(从新分配)阶段的开始的时间称为“迫近度(imminence)”
图14. 迫近度(图字:标记 暂停、迫近度增加)
public static function pauseForGCIfCollectionImminent(imminence:Number = 0.75):void
是Flash Player 11和AIR 3中的一个新方法,容许您通知垃圾收集器这是完成标记和执行收集的好时机(ActionScript参考文档中的API项)。计划在用户不会注意到时发生可能的暂停,这会带来更好的用户体验。例如,一个游戏可能在游戏中一个级别完成时调用此函数,进而减小在玩游戏期间发生暂停的机会。
您传递给此方法的迫近度值用于与垃圾收集器处于标记阶段中的位置进行比较。若是您传递给它的值比垃圾收集器的迫近度值小,标记和清除将同步完成并致使应用 程序暂停。垃圾收集器必须处于该过程的25%以上,才能响应这个暂停以进行收集的请求。传递一个较小的值(但大于0.25)极可能会强制执行收集,致使应 用程序暂停。传递一个较大的值将告诉垃圾收集器只有在即将暂停时完成收集。
延伸阅读
理解内存管理和垃圾收集在Flash Player和AIR中的工做原理,将有助于您优化您的代码并开发更高性能的应用程序。请查阅Michael Labriola介绍垃圾收集的演示Talking Trash。阅读Christian Cantrell的Providing Hints to the Garbage Collector in AIR 3。您也能够阅读详细的MMgc讨论,其中包含对底层C++代码的描述。
查看原文:Garbage collection internals for Flash Player and Adobe AIRhtml
转:http://bbs.5aser.com/forum.php?mod=viewthread&tid=377git