所有应用软件都需要管理内存,一个应用软件的内存管理系统包括了如下准则:什么时候派发内存,要派发多少内存,什么时候把东西放到回收站,以及什么时候清空回收站。MMgc是Flash Player几乎所有内存分配的通用内存管理器,知道MMgc是如何管理内存的对优化你的代码以及运行程序都很重要。

内存被垃圾回收机制自动回收就叫做“托管内存”。垃圾回收机制确定当内存不再被应用程序用到的时候就进行回收。本文研究的是内存分配、垃圾回收机制进程以及在Flash Player 11和AIR 3中的新API,pauseForGCIfCollectionImminent()。

内存分配

Flash Player使用页面分配器(GCHeap)从操作系统(OS)中分配大量的空白存储空间(百万字节)
。然后GCHeap将这大量的存储空间分裂成更小的4K页面并根据需要把这些页面交给垃圾回收机制(GC)的内存管理器。


图1. GCHeap从OS分配内存,将其分裂成4K页面,交给GC。

然后GC用这些4K页面来给系统中大小达2K的对象提供内存空间。


图2. 4K个页面都被GC分配给大小小于2K的对象。

对于大于2K的对象(位图、视频、文件等),GCHeap为存储分配器提供了大量接近4K的空白存储。

当一大区块中几乎所有的4K页面都得到分配时,Flash Player就会在GCHead试图从OS中分配更多内存空间之前,运行GC来回收不再使用的内存。换句话说,GC只因内存分配而触发。在测试以及程序概要分析中记住这一点很重要,因为它意味着一个空转程序的内存使用永远都不会发生改变。

Heap和Stack

Heap是为任何已经创建的或在运行过程中初始化的对象分配的内存空间。对象在被回收之前都会一直存在于Heap中。


图3. 对象A存在于Heap中,被Stack中的局部变量o引用。

Stack是存储所有在编译期定义的变量的内存空间。Stack内存主要是以连续性的方式被使用且重复使用,push可以给Stack首位进行添加,pop则从Stack首位移除,要在Stack中间插入的唯一途径是把这一位置上边部分清空。

局部方法变量、参数以及当一个方法函数结束时返回的位置信息,都要在方法函数运行时push到Stack里。Stack改变是很快的,其对象引用时间往往很短。对象引用可以在Stack中存在,但对象的内存分配就来自Heap。


图4. 局部变量在声明的时候就push到Stack中去,当方法函数运行完返回的位置信息也会push到Stack里。

Flash运行过程中垃圾回收机制的执行
Flash Player和AIR
使用了延缓引用计数器与传统的标记/清除器两者的组合体。

·延缓引用计数器

在延缓引用计数器中,存在着Heap与Stack两者的引用差别。因为Stack变化得很迅速并且往往包含了短暂的引用,所以Stack引用中不使用引用计数器,而Heap引用就会保留并累加。


图5. 对象跟踪了解它们的引用次数。

Heap中的每个对象都掌握了指向它本身的事件数目,每次为一个对象创建引用时,对象的引用次数就会增加,当你删除一个引用,对象的引用次数就会减少,如果对象的引用数为零(没有任何事件指向它),它就会加入到零计算表(ZCT)。当ZCT满了,Stack就在ZCT中扫描查找从Stack到对象的引用,在ZCT中所有没有Stack引用的对象都会被删除。

延缓引用计数的其中一个问题是循环引用,如果ObjectA跟ObjectB都指向对方但系统中又没有其他对象指向它们,那么它们就不会有零引用次数并且使用引用计数器的时候它们就不会被垃圾回收机制选中,这就是标记/清除垃圾回收器使用之处。


图6. ObjectA与ObjectB相互引用但没有其他引用参数。

·Mark/Sweep

Flash Player或AIR里运行的应用程序都有多重GCRoot,你可以把GCRoot看作树干而应用程序的对象当成树枝,舞台是一个GCRoot,加载器是GCRoot,固定菜单是GCRoot,所有在使用的程序对象在程序内部都可由GCRoot获得,GCRoot永远不会被回收。

程序中的所有对象都有一个“标记位”,当垃圾回收机制的Mark阶段开始时,所有标记位都被清空,MMgc负责跟踪了解所有程序中的GCRoot,垃圾回收器从根目录开始,输出每个对象并为它接触到的所有对象设置标签位,在根目录中不可获得的对象在程序中也就不可获取了——它的标记位在Mark阶段也不能获取和设置,一旦回收器为所有找到的对象完成了标记,Sweep阶段也就开始了。没有设置标记位的对象都会删除,其内存也会回收。


图7. 在循环引用中的对象不被标记

图7显示了在GCRoot中每个可获取的对象都有自己的标记位设置(蓝色部分),另外两个对象(ObjectA和ObjectB)困在了一个循环引用中而不能被GCRoot获取到,它们的标记位不能被设置,所以即使它们不是零引用次数,这两个对象也会被回收。

·弱引用

Flash Player也可以为确定类型的对象保留引用,这叫做“弱引用”。弱引用在垃圾回收器中对于正常追踪进程(通过跟踪根目录来找到可获取的对象的过程)是不可访问的。

当你实例化一个新的Dictionary时,你可以指明你要它进行弱绑定的Dictionary字段。

  1. var d:Dictionary = new Dictionary( true );
  2. d[ someObject ] = someValue;

复制代码

你也可以在添加事件侦听时设置addEventListener()函数的useWeakReference参数为真。

  1. obj.addEventListener( "type", handler, false, 0, true );

复制代码

在上述两段中,你让Flash Player在两个对象中保留一个引用但是以比较弱的方式保留。事实上这意味着这个特殊的引用在标记过程中不会被追踪。


图8. 弱引用在标记的过程中不会被追踪。

这么一来通往ObjectB的惟一路径就会比较弱,在追踪过程中就不会运行因此ObjectB就不会标记到以致于被回收。然后,如果通往ObjectB有比较强一些的路径,ObjectB就会被标记并保留。


图9. 强引用的对象会在追踪过程被发现并标记。

不再使用引用时,就应当进行清空,只需用removeEventListener()移除Dictionary上不再使用的条目就可以了。但是,有时候清空无用的引用是不切实际或不可能的。比方说当一个类实例化了并在你不知道的时候就删除了——条目渲染器就是如此。这样,为了让对象的弱引用保留,就会允许Flash Player最终将它们移除并回收内存。

·保守型的回收器
MMgc被认为是标记/清扫的一个保守型的回收器,MMgc无法确定定内存中的某些量是对象指针(内存地址)还是数值,为了防止意外的把某些值所指向的对象回收,MMgc把每个值都当成指针,这样,一些确实没有被指向的对象就永远不会被回收并被当成内存泄漏,即使你想减少内存泄漏来进行优化,这些由保守的GC产生的偶尔出现的泄漏是随机出现的,不随时间变化,且对应用程序的影响比开发者的人为泄漏影响要小得多。

增长型的回收

很不幸垃圾回收机制会使Flash Player随着进程完成而发生周期性暂停,这种暂停与应用程序当前使用的内存数量是成比例的,它会比想象中更长时间并且在某些程序中是可以观察到的。

在垃圾回收过程中Mark阶段是最密集的,因此,标记过程在增长的时候使用了工作队列和三色算法,队列保留了标记增长值的标记状态。

表1. 三色算法
黑色对象被标记且不再在队列中
灰色对象在队列中但还没被标记
白色对象不被标记也不在队列中

在Mark阶段开始时,所有GCRoot都被放到队列中且置灰。


图10. GCRoot在放入工作队列时置灰。

Mark进程继续,标记对象变成黑色并从工作队列中移除。


图11. 标记对象是黑色的且不再位于工作队列中。

进程毫无疑问的继续直到一个新的对象(白色的)添加到黑色对象中,然后,白色对象不再拥有标记位设置,因为它们的GCRoot已经被标记了,没有了标记位,它们就会在Sweep阶段被回收。


图12. 新的对象被添加到先前标记的对象中。

为了避免这个问题发生,MMgc中“写屏障”就被用来防止任何的白色对象在添加到黑色对象上时马上就转入工作队列中。


图13. 新对象添加到先前标记的对象时就马上进入工作队列中。

使用工作队列和三色算法,可以控制Mark阶段的启动和停止来防止垃圾回收机制大规模、不必要的停顿。

紧逼性

Mark阶段是垃圾回收机制中最密集的部分,但事实上清空垃圾—分配释放的内存—也是需要时间的,内存分配也会使应用程序停顿。垃圾回收器完成Mark阶段并开始Sweep(分配内存)所需的时间就叫紧逼性。


图14. 紧逼性

公共静态函数pauseForGCIfCollectionImminent(imminence:Number = 0.75):void是Flash Player 11和AIR 3中一个新的方法,它允许你访问垃圾回收器,这有益于完善标记过程和执行回收(ActionScript参考文档中的API词条)。在用户未发现可能发生的停顿之前就将其按时间列出,会有比较好的用户体验。举个例子,游戏程序可能会在完成一个关卡时调用这个函数,然后就可以在游戏过程中减少停顿发生的可能性。

你传递给这个方法紧迫值与垃圾回收器在Mark阶段的位置进行比较。如果你传递的值比回收器的紧迫值小,Mark和Sweep就会同步完成并引起程序的停顿。在察觉到回收器停顿的需求之前,垃圾回收器在进行中至少占25%的进程。传递一个较小的值(尽管大于0.25)将更能强制回收且导致程序停顿,传递一个较大的值会使回收器无论如何只在即将发生的时候完成回收。

从这里出发

理解Flash Player和AIR里的内存管理器与垃圾回收机制如何工作,可以帮助你优化你的代码并开发效果更好的应用程序。查看Michael Labriola关于垃圾回收机制的报告Talking Trash。阅读Christian Cantrell的Providing Hints to the Garbage Collector in AIR 3。你也可以看一下MMgc的详细讨论,它包括了基础的C++代码的一些描述。

原文链接:http://www.adobe.com/devnet/actionscript/learning/oop-concepts/garbage-collection.html

[转] gc tips(1)的更多相关文章

  1. [转] gc tips(3)

    原文地址:http://kevincao.com/2011/08/actionscript-garbage-collection-2/ 谈谈ActionScript垃圾回收(下) 前文我们介绍了GC的 ...

  2. [转] gc tips(2)

    原文地址:http://kevincao.com/2011/08/actionscript-garbage-collection-1/ 谈谈ActionScript垃圾回收(上) 在<给AS程序 ...

  3. Best Practices for Performance_1、2 memory、Tips 性能和小的优化点、 onTrimMemory

    http://developer.android.com/training/articles/memory.htmlhttp://developer.android.com/tools/debuggi ...

  4. Java Hotspot G1 GC的一些关键技术

    G1 GC,全称Garbage-First Garbage Collector,通过-XX:+UseG1GC参数来启用,作为体验版随着JDK 6u14版本面世,在JDK 7u4版本发行时被正式推出,相 ...

  5. Understanding CMS GC Logs--转载

    原文地址:https://blogs.oracle.com/poonam/entry/understanding_cms_gc_logs Understanding CMS GC Logs By Po ...

  6. How-to go parallel in R – basics + tips(转)

    Today is a good day to start parallelizing your code. I’ve been using the parallel package since its ...

  7. Android应用程序性能优化Tips

    对于我们设计的应用需要做到以下特征:build an app that's smooth, responsive(反应敏捷), and uses as little battery as possib ...

  8. golang ----gc问题

    go程序内存占用大的问题 这个问题在我们对后台服务进行压力测试时发现,我们模拟大量的用户请求访问后台服务,这时各服务模块能观察到明显的内存占用上升.但是当停止压测时,内存占用并未发生明显的下降.花了很 ...

  9. golang 垃圾回收 gc

    http://ruizeng.net/golang-gc-internals/ 摘要 在实际使用go语言的过程中,碰到了一些看似奇怪的内存占用现象,于是决定对go语言的垃圾回收模型进行一些研究.本文对 ...

随机推荐

  1. js截取指定字节长度的字符串

    默认的截取字符串都是根据字符长度或位置截取的,典型的两个方法是substr和substring. 这样导致的问题是截取同样长度的字符串时,多字节字符(汉字等)和单字节字符(半角英文字母.半角数字)占的 ...

  2. poj3415 Common Substrings(后缀数组,单调栈 | 后缀自动机)

    [题目链接] http://poj.org/problem?id=3415 [题意] A与B长度至少为k的公共子串个数. [思路] 基本思想是将AB各个后缀的lcp-k+1的值求和.首先将两个字符串拼 ...

  3. ext3grep

  4. 307. Range Sum Query - Mutable

    题目: Given an integer array nums, find the sum of the elements between indices i and j (i ≤ j), inclu ...

  5. 306. Additive Number

    题目: Additive number is a string whose digits can form additive sequence. A valid additive sequence s ...

  6. 指定IE浏览器渲染方式

    <meta http-equiv="X-UA-Compatible" content="IE=7" />以上代码告诉IE浏览器,无论是否用DTD声明 ...

  7. linux 命令案例学习——文件搜索

    两个搜索文件的工具 locate  ——仅仅通过文件名查找文件 find     ——依据文件的各种属性在既定目录(包括子目录)里查找 一个通常与文件搜索命令一起使用.处理搜索结果文件列表的命令 xa ...

  8. Mybatis SqlSessionTemplate 源码解析

    As you may already know, to use MyBatis with Spring you need at least an SqlSessionFactory and at le ...

  9. php mysql_insert_id()

    mysql_insert_id mysql_insert_id()返回给定的 link_identifier中上一步 INSERT 查询中产生的 AUTO_INCREMENT 的 ID 号.如果没有指 ...

  10. 深入理解Java内存模型(七)——总结

    处理器内存模型 顺序一致性内存模型是一个理论参考模型,JMM和处理器内存模型在设计时通常会把顺序一致性内存模型作为参照.JMM和处理器内存模型在设计时会对顺序一致性模型做一些放松,因为如果完全按照顺序 ...