翻译:Flash Player 9资源管理策略,欢迎指正

关键字: flash player


原文

Resource management strategies in Flash Player 9

 

翻译

ActionScript 3.0给Flash开发人员带来了更快的代码运行速度以及很多API改进。从开发人员角度看,这些改进的出现要求(开发人员)具备更多的责任。本文着重讨论ActionScript 3.0在资源管理特性方面的含义,并简单介绍一个工具以帮助你更有效的跟踪、管理内存。

ActionScript 3.0新显示列表模型是对资源管理影响最大的改变。在Flash Player 8及之前版本中,当一个display对象从屏幕被移除时(使用removeMovie 或 unloadMovie)时,该对象及其子对象被立即从内存移除并且代码终止运行。Flash Player 9引入了更加灵活的显示列表模型,该模型将display对象(sprites、movie clips等)作为普通对象一样对待。

这 意味着开发人员现在可以做一些很酷的操作,如重排根目录(将显示对象从一个显示列表移到另一个中);从已经载入的SWF中实例化display对象。不幸 的是,这同时意味着display对象与所有其他对象一样将被垃圾收集器同等对待,由此导致大量有趣的(可能不太明显的)问题。

为什么资源管理是个问题

Flash开发人员看到ActionScript 3.0中这些新的资源管理考虑之后,大概觉得这些概念有些复杂。而Java开发人员可能觉得这很正常。这种差距可以理解:Flash开发人员除了基本最佳实践还不习惯手工方式实现资源管理——例如删除不再使用的引用——然而Java开发人员之前就这么做了。这些问题对多数现代有内存管理的语言也是家常便饭,糟糕的是现在没办法完全避免它。

虽然资源管理是现实,可Flash还是带来了很多挑战,这在其它语言中很少见(包括Flex)。Flash内容区试图加载大量空闲或非活动的代码, 而不是像Java和Flex一样:CPU敏感的代码只在用户操作时执行。还有,相对其它平台,Flash项目总是加载太多的第三方代码(很可能使用糟糕的 编码规范)。Flash开发者也没什么工具、profiler和框架可用。

最后,Flash开发者通常没有正规的编程背景。我知道的Flash开发者有音乐、艺术、商业、哲学或除了编程之外的其它背景。开发者背景的多样性致使创新和成果令人敬畏,但Flash社区开发者还没有准备好处理资源管理问题。

问题1: Dynamic content

一个比较明显的资源管理问题与sprite(或其它display对象)有关:你动态实例化一个显示对象,并希望不久后它被删除。但显示对象将存在于内存中即使你已经把它从stage移除,因为它们不再由显示列表管理生命周期。如果你没有清除这个clip的所有引用,包括该对象上的监听器,它永远不会被删除;如果你认真的清除了所有引用,该对象将会在垃圾回收器下一次执行清理工作时被回收。这个工作在内存使用不紧张的未来不确定的某时刻发生。<o:p></o:p>

有一点非常重要:不仅仅是显示对象使用内存,任何其它“空闲”代码例如定时器、enterFrame和其外部监听器,都会占用内存。<o:p></o:p>

举几个例子有助于演示这个问题:
  • 一个游戏sprite订阅了它注册的enterFrame事件(一个sprite由很多frame组成,在切换到下一帧触发enterFrame事件,译者注)。移动一帧时,应用程序执行一些计算来大致确定一下游戏元素。在ActionScript 3.0中,每次你从显示对象列表删除sprite后把它的所有引用设置为null,应用程序还继续执行这一帧的代码直到被垃圾回收器删除。所以你必须在删除sprite对象时显式删除其enterFrame监听器。
  • 考 虑一个Movie Clip,它通过注册Stage的mouseMove事件响应。这是在新显示列表模型中实现该效果的唯一方法。除非你记得删除该监听器,否则这clip将 在鼠标每次移动时继续执行,即使它已经被“删除”。默认情况下,这个clip总是会执行因为有一个它的引用存在于Stage的事件派发器。我在以后的文章 会讨论如何避免这个问题。

现在想象一下初始化若干sprite对象并在垃圾回收器执行清除之前删除它的含义,或者如果你删除所有引用失败会发生什么。你可以毫不留神的轻松耗 光CPU资源并导致程序或游戏慢的像爬虫,或者几乎完全使客户机器停止。目前没有办法强制Flash Player去删除一个显示对象并停止其执行。在这些对象从画布移除后需要Flash开发者用手工方式删除。

问题 2: Loaded content

要记住一点,已加载的SWF文件的内容现在和其它所有对象一视同仁,你能想象的到已加载内容可能遇到的问题。和其它的显示对象一样,没有办法显式的 从内存移除一个已加载的SWF文件和它的内容,或停止其代码运行。调用Loader.unload只是简单把到加载的SWF的引用置null;它(已加载 内容,译者注)将继续存在并执行直到它被下一次垃圾回收器执行清除操作时处理为止(假设到已加载内容的所有引用都已经被正确的清除)。

考虑下列两个场景:

  • 你创建了一个外壳程序(原文shell,此处译为外壳程序较为合理)来加载你的试验Flash项目。这个前沿的试验性作品将CPU资源的使用发挥至极限。用户点击按钮来加载一个试验作品并查看它,然后点击按钮加载第二个试验。即使所有到第一个试验作品的引用都被清除,它还是会在后台运行,如果与此同时第二个试验作品启动运行,这将耗光处理器资源。
  • 谋客户委任你创建一个应用程序加载由其它开发人员制作的ActionScript 3.0 SWF。他们把一些监听器添加到Stage或者引用一些外部资源到自己的程序。因为没办法卸载(外部资源),它们将会待在内存中继续消耗CPU资源直到用户退出程序。即使已加载内容没有任何外部引用,他们还是会继续运行直到垃圾收集器清理它们。

当你设计一个应用程序去加载未信任的内容,意识到这些内容在你卸载之后还在继续执行很重要。这些内容按照相应的Flash Player 安全模型运行。在开发阶段就深入考虑程序中潜在的问题总是一个好主意。

使用 System.totalMemory

虽然System.totalMemory是个简单的工具,重要的是它是Flash开发者可以使用的第一个运行时profiling工具。它允许你检测Flash Player运行时刻占用多少内存。这使你有了一定能力在开发阶段profile程序,而不是使用系统监视器。更重要的是,它让你有可能在程序给客户造成严重事故前优先处理重大的内存泄露问题。抛出一个错误或终止你的程序总比使客户系统停滞完全停止要好的多。

有一个简单例子可以解释如何实现此功能:

         
         
         
         
ActionScript 代码
 
  1. import flash.system.System;  
  2. import flash.net.navigateToURL;  
  3. import flash.net.URLRequest;  
  4. ...  
  5. // check our memory every 1 second:  
  6. var checkMemoryIntervalID:uint = setInterval(checkMemoryUsage,1000);  
  7. ...  
  8. var showWarning:Boolean = true;  
  9. var warningMemory:uint = 1000*1000*500;  
  10. var abortMemory:uint = 1000*1000*625;  
  11. ...  
  12. function checkMemoryUsage() {  
  13.    if (System.totalMemory > warningMemory && showWarning) {  
  14.       // show an error to the user warning them that we're running out of memory and might quit  
  15.       // try to free up memory if possible  
  16.       showWarning = false// so we don't show an error every second  
  17.     } else if (System.totalMemory > abortMemory) {  
  18.       // save current user data to an LSO for recovery later?  
  19.       abort();  
  20.     }  
  21. }  
  22. function abort() {  
  23.    // send the user to a page explaining what happpened:  
  24.    navigateToURL(new URLRequest("memoryError.html"));  
  25. }  

显然这段代码还有很多改进的余地。但希望它能演示这个功能背后的主要概念。

谨记,totalMemory是一个进程内共享值。一个单独进程可能就是一个浏览器窗口,或者所有打开的浏览器窗口,或依赖于浏览器或操作系统以及窗口如 何打开的。举个例子,在Mac OS X中,所有浏览器窗口共享单一进程以及totalMemory值,而在微软Window操作系统中,进程和totalMemory的情况就复杂多了。

弱引用

ActionScript 3.0中有一个特性我很喜欢,这就是弱引用。可以这样描述:它们是某对象的引用但垃圾回收器在检测该对象的活动能力时不被当作引用计数。如有某对象只拥有唯一的引用并且该引用是弱引用,这个对象就会被垃圾回收器在下一轮执行时清除。

可惜的是,弱引用只能在两种场景下使用。第一个应用场景是事件监听器,也是最大的应用场合。因为事件监听器是造成垃圾回收器出现问题的常见引用类型 之一。我强烈建议永远对事件监听器使用弱引用。只须在你调用addEventListener 方法时把第5个参数设置为true,如下所示:

         
         
         
         
ActionScript 代码
 
  1. someObj.addEventListener("eventName",listenerFunction,useCapture,priority,weakReference);  
  2. stage.addEventListener(Event.CLICK,handleClick,false,0,true);  
  3. // the reference back to handleClick (and this object) will be weak.  

了解更多关于弱引用的资料,请阅读我blog的这篇文章 weakly referenced listeners.

ActionScript 3.0的Dictionary对象也支持弱引用。创建新Dictionary对象时传true给第一个参数就可以轻松使该对象对其key使用弱引用,如下所示:

         
         
         
         
ActionScript 代码
 
  1. var dict:Dictionary = new Dictionary(true);  
  2. dict[myObj] = myOtherObj;  
  3. // the reference to myObj is weak, the reference to myOtherObj is strong  

这里通往何方

资源管理是ActionScript 3.0开发中的重要问题。忽略本文描述的这些问题,可能导致反应迟钝的应用还有使客户系统完全停滞的潜在风险。没有任何方法能显示从内存删除一个显示对象并停止其代码运行。这意味着所有Flash开发人员有义务去正确清除程序中不再使用的对象。

ActionScript 3.0实际上增加了开发人员的工作,他们必须对其程序进行资源管理。Flash Player 9提供了一些新工具帮助开发人员进行内存管理。配合这些工具以及有效的策略和途径(我的系列文章,理解Flash Player 9垃圾收集机制)可使你在未来的Flash和Flex项目中成功的进行资源管理。

更多信息,请访问Flash开发者中心以及Flash Player开发者中心。

 关于作者

Grant Skinner是gskinner.com公司CEO及架构师,该公司是一个Flash开发和咨询公司。他与新媒体机构以及有进取心的客户合作开发前沿应 用、游戏和多媒体程序。他擅于将支持接口设计的代码、可用性、市场和业务逻辑融汇,由此带给他国际上赞誉并获得包括Best Canadian Developer at FITC 2005在内的多个业界大奖。Grant在gskinner.com/blog/写blog并在incomplet.org展示他的验证性的创作。

你可能感兴趣的:(游戏,项目管理,Flex,Flash,actionscript)