浅谈Flash的垃圾回收机制

关于Flash内存清理
服务器君一共花费了281.796 ms进行了6次数据库查询,努力地为您提供了这个页面。
试试阅读模式?希望听取您的建议

Flash Player的garbage collection(GC)分两种运行方式,一种是“引用计数法”(Reference Counting),一种是“标记-清除法”(Mark Sweeping)。

引用计数法是通过计算指向某个对象的引用的数量来确定是否清除该对象。如果一个对象的引用数量为0,表示程序无法再访问到该对象,则清除该对象;如果引用计数不为0,则不清除。这种方法运行代价较小,但是这种方法无法清除存在循环引用关系的对象集合。标记-清除法是从程序的根对象开始,遍历每个引用指向的对象。遍历经过的对象,则将其标记。最后清除所有没有打上标记的对象。这种方法比较彻底,但是运行代价较高。

FlashPlayer运行GC的时间并不固定,它会根据你的内存的占用情况来决定运行GC的时间。它会根据用户机器的内存值来设定一个阀值,然后将程序的占用内存量保存在该阀值左右。

正因为FlashPlayer这种“不确定”的GC机制,所以我们所要做的主要工作是确保创建的对象在不需要的时候可以被释放。确保对象可以被释放的大原则是没有外部引用指向该对象,除了一般情况下的没有将外部引用显示地设为null之外,以下的情况也会导致对象无法释放:

  1. 没有remove监听的事件。比如,A对象对某个事件进行监听,监听函数(Event Handler)存在于B对象中,则相当于A对象会保存一个B对象的方法的引用,会导致B对象的内存无法释放。
  2. 解决方法:注意remove掉监听事件;或者在调用addEventListener()时,将监听函数设为弱引用,但这种做法只适合一次性的监听。

  3. 使用BindingUtils.bindSetter()、ChangeWatcher.watch()绑定某个对象之后,没有清除该绑定。道理同1,其实绑定某个对象,也就是监听其发出的PropertyChange事件。
  4. 解决方法:使用ChangeWatcher.unwatch()来清除绑定关系。

  5. 声明了样式,并在样式中使用了嵌入式资源。比如在标签中定义了样式名称。一个对象定义了样式,相当于对外声明了一个全局可用的样式,因此会到导致外部保存了该对象的引用,可能导致对象无法释放。
  6. 解决方法:解决方法很多,可以使用动态加载的样式,或者使用一个类或模块(Module)专门管理样式,这些解决方法取决程序的架构设计。

  7. 使用ExternalInface.callBack()声明了对外的API函数。类似于情况1,一个对象对外声明了API,就使外部保存了指向该对象的引用。
  8. 解决方法:如果之前使用了ExternalInface.callBack("APIName", functionName)声明了一个API,则可以使用ExternalInface.callBack("APIName", null)取消该API。

  9. 某些控件(类似TextInput),或者由这类控件构成的自定义组件,当焦点在这些控件上时,即使从DisplayList移除掉这些控件并删除引用,这些控件对象也无法释放。这个问题还有人提出来是一个Bug(http://bugs.adobe.com/jira/browse/SDK-14781)。这个问题估计和flash的焦点管理机制有关。
  10. 解决方法:目前的解决方法只能是等焦点重新转移到其他控件上(比如点击了其他控件),如此之前的控件对象就可以被GC释放。

那应该在什么时候做好垃圾清理的准备工作呢?之前有的文章说应该监听组件的removeFromStage事件,在其处理方法中进行垃圾清理的准备工作(清除引用,删除监听器,清除绑定关系,取消对外API等工作)。

其实这种方法不太确切。因为removedFromStage事件是当组件从DisplayList上移除的时候发出的,并不代表该组件对象的生命周期已经终结。只要程序保留了该组件对象的引用,可以再重新把该组件对象添加到DisplayList上(此时,该组件对象会发出addedToStage事件)。如果单纯在removedFromStage事件的监听函数中做该对象的垃圾清理准备工作,当组件重新被使用的时候,可能导致该组件对象原来的状态被破坏而无法使用。

因此,比较好的实践方法应该是,利用addedToStage、removedFromStage两个事件的对应关系,在removedFromStage事件的处理方法中执行垃圾清理的准备工作(清除引用,删除监听器,清除绑定关系,取消对外API等作),而在addedToStage事件的处理方法中执行removedFromStage事件的处理方法的反操作(设置引用、添加监听、设定绑定关系、设置API...也可以认为是一个组件对象的初始化操作),这样就可以保证一个组件对象被从DisplayList上移除后,可以释放相应内存;如果保存其引用,并将其重新添加到DisplayList上,又可重新使用。

最后翻译一段关于内存清理的建议:

  1. usage of instance members instead of static members can easily be detected with the profiler (replace by static members where possible)
  2. 使用实例成员(instance members),而不是用静态成员(static members),可以更容易地被profiler检查到。因此,尽可能地使用实例成员,而不要用静态成员。

  3. usage of weak references and / or removal of eventListeners after consumption of the event (if posible) helps reducing the memory usage
  4. 在事件完成之后,将其设为引用 而且/或者(and / or) 将其remove掉,有助于减少内存使用。

  5. moduleLoader.unloadModule leaks memory, use moduleLoader.url=null instead
  6. moduleLoader.unloadModule()会导致内存泄露,因此建议使用将moduleLoader.url=null.

  7. module memory is freed at arbitrary times (not at unload)
  8. module内存的释放时间是不确定(并不是在unload的时候)。

  9. runnning debug version of modules leaks huge amounts of memory no matter which container is used
  10. 使用debug版本的module会导致大量的内存泄露,不管其容器是否使用。

  11. declaring modules as modules in the configuration of a flex builder 3 project (and not as applications like in FlexBuilder 2) and optimizing for a specific application reduces module size drastically
  12. 将一个程序块声明为module,而不要将其声明为application,并且设置各module专门为一个application进行优化,能大量节约内存。

  13. forcing garbageCollection (double LocalConnection.connect hack) is necessary in order to measure leaks and to keep memory under control
  14. 在适当的时候,为了内存可控,可强制使用垃圾收集器(garbageCollection),方法如下:

    try {
    	import flash.net.LocalConnection;
    	var conn1:LocalConnection = new LocalConnection ();
    	var conn2:LocalConnection = new LocalConnection ();
    	conn1.connect("gc");
    	conn2.connect("gc");
    }catch(e:Error){}    
  15. use the release version of the module swf
  16. 使用release版的module swf。

  17. uninstall the debug flash player ("uninstall_flash_player.exe")
  18. 卸载debug版的flash player。

  19. install the release version of the flash player ("install_flash_player_active_x.msi")
  20. 安装release版的flash player。

本文地址:http://www.nowamagic.net/librarys/veda/detail/1131,欢迎访问原出处。

不打个分吗?

转载随意,但请带上本文地址:

http://www.nowamagic.net/librarys/veda/detail/1131

如果你认为这篇文章值得更多人阅读,欢迎使用下面的分享功能。
小提示:您可以按快捷键 Ctrl + D,或点此 加入收藏

大家都在看

阅读一百本计算机著作吧,少年

很多人觉得自己技术进步很慢,学习效率低,我觉得一个重要原因是看的书少了。多少是多呢?起码得看3、4、5、6米吧。给个具体的数量,那就100本书吧。很多人知识结构不好而且不系统,因为在特定领域有一个足够量的知识量+足够良好的知识结构,系统化以后就足以应对大量未曾遇到过的问题。

奉劝自学者:构建特定领域的知识结构体系的路径中再也没有比学习该专业的专业课程更好的了。如果我的知识结构体系足以囊括面试官的大部分甚至吞并他的知识结构体系的话,读到他言语中的一个词我们就已经知道他要表达什么,我们可以让他坐“上位”毕竟他是面试官,但是在知识结构体系以及心理上我们就居高临下。

所以,阅读一百本计算机著作吧,少年!

《深入理解MySQL核心技术》 Sasba Pacbev (作者), 李芳 (译者), 于红芸 (译者), 邵健 (译者)

《深入理解MySQL核心技术》:从公共可用性的意义上讲,MySQL源代码是开放源代码,但如果对其不了解,则实质上,它对于您来说是封闭的。MysQL开发团队的前成员Sasha Pachev通过《深入理解MySQL核心技术》给出了MySQL 5的全面指南,揭示了这一强大数据库的内部运作。您将直奔MySQL核心技术,了解各种数据结构和各种方便的功能的运作情况,了解如何添加新的存储引擎和配置选项等。 《深入理解MySQL核心技术》从结构概况讲起,在这一部分解释了MysQL的不同组件是如何协同工作的。接着将学习设置有效的可编译代码副本的步骤,然后使用基本架构添加自己的配置变量和存储引擎。

更多计算机宝库...