优化及涉及与清理

关于Flash内存

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

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

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

详细可查看文章“Understanding garbage collection in Flash Player 9”。

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

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

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

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

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

5. 某些控件(类似TextInput),或者由这类控件构成的自定义组件,当焦点在这些控件上时,即使从DisplayList移除掉这些控件并删除引用,这些控件对象也无法释放。这个问题还有人提出来是一个Bug(http://bugs.adobe.com/jira/browse/SDK-14781)。这个问题估计和flash的焦点管理机制有关。
解决方法:目前的解决方法只能是等焦点重新转移到其他控件上(比如点击了其他控件),如此之前的控件对象就可以被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. usage of weak references and / or removal of eventListeners after consumption of the event (if posible) helps reducing the memory usage

3. moduleLoader.unloadModule leaks memory, use moduleLoader.url=null instead

4. module memory is freed at arbitrary times (not at unload)

5. runnning debug version of modules leaks huge amounts of memory no matter which container is used

6. 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

7. forcing garbageCollection (double LocalConnection.connect hack) is necessary in order to measure leaks and to keep memory under control

8. use the release version of the module swf

9. uninstall the debug flash player ("uninstall_flash_player.exe")

10. install the release version of the flash player ("install_flash_player_active_x.msi")

以下是翻译内容:

1. 使用实例成员(instance members),而不是用静态成员(static members),可以更容易地被profiler检查到.因此,尽可能地使用实例成员,而不要用静态成员.

2. 在事件完成之后,将其设为引用 而且/或者(and / or) 将其remove掉,有助于减少内存使用.

3. moduleLoader.unloadModule()会导致内存泄露,因此建议使用将moduleLoader.url=null.

4. module内存的释放时间是不确定(并不是在unload的时候).

5. 使用debug版本的module会导致大量的内存泄露,不管其容器是否使用.

6. 将一个程序块声明为module,而不要将其声明为application,并且设置各module专门为一个application进行优化,能大量节约内存.

7. 在适当的时候,为了内存可控,可强制使用垃圾收集器(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){}

注意:这是FP9时的方法在FP10及以后已经有的System.GC();

8. 使用release版的module swf.

9. 卸载debug版的flash player.

10. 安装release版的flash player.

参考文档:

1. Understanding garbage collection in Flash Player 9(http://www.adobe.com/devnet/flashplayer/articles/garbage_collection.html)

2. FLEX内存释放优化原则(http://xinsync.xju.edu.cn/index.php/archives/1825)

3. Garbage Collection and Memory Leaks(http://blogs.adobe.com/aharui/2007/03/garbage_collection_and_memory.html)

4. memory leak when using TextInput and TextArea when click the keyboardhttp://bugs.adobe.com/jira/browse/SDK-14781

 

=================

 

AS3相对于以前版本的功能增强了很多,在赋予它重任时,同时也要它付出代价:垃圾收集器不再支持自动为你收集垃圾。本文中,我为大家整理了一些资料。首先,我们先来了解下垃圾收集器是个什么东西?     
       (1)关于垃圾收集器
         垃圾收集器是一个后台进程它负责回收程序中不再使用的对象占用的内存。非活动对象就是不再有任何其他活动对象引用它。为便于理解这个概念,有一点非常重要,就是要意识到除了非原生类型(Boolean, String, Number, uint, int除外),你总是通过一个句柄访问对象,而非对象本身。当你删除一个变量其实就是删除一个引用,而非对象本身。
       (2)AS3的内存机制的方法:
         <a>引用计数法:引用计数法是一种用于跟踪活动对象的较为简单的方法,它从ActionScript1.0开始使用。当你创建一个指向某个对象的引用,该对象的引用计数器加1;当你删除该对象的一个引用,该计数器减1.当某对象的计数器变成0,该对象将被标记以便垃圾回收器回收。
        
     var a:Object = {foo:"bar"}
  // the object now has a reference count of 1 (a)
  var b:Object = a;
  // now it has a reference count of 2 (a & b)
  delete(a);
  // back to 1 (b)
  delete(b);
  // down to 0, the object can now be deallocated by the GC
 
         <b>引用计数法简单,它不会非CPU带来巨大的负担;多数情况下它工作正常。不幸地是,采用引用计数法的垃圾回收器在遇到循环引用时效率不高。循环引用是指对象交叉引用(直接、或通过其他对象间接实现)的情况。即使应用程序不再引用该对象,它的引用计数器仍然大于0,因此垃圾收集器永远无法收集它们。下面的代码演示循环引用:
      
     var a:Object = {}
  // create a second object, and reference the first object:
  var b:Object = {foo:a};
  // make the first object reference the second as well:
  a.foo = b;
  // delete both active application references:
  delete(a);
  delete(b);
         上述代码中,所有应用程序中活动的引用都被删除。我没有任何办法在程序中再访问这两个对象了,但这两个对象的引用计数器都是1,因为它们相互引用。循环引用 还可以更加负责 (a 引用 c, c引用b, b引用a, 等等) 并且难于用代码处理。FlashPlayer 6 和 7的XML对象有很多循环引用问题: 每个 XML 节点被它的孩子和父亲引用,因此它们从不被回收。幸运的是FlashPlayer 8 增加了一个叫做标识-清除的新垃圾回收技术。
          <c>标识-清除法 ActionScript3.0 (以及FlashPlayer 8) 垃圾回收器采用第2种策略标识-清除法查找非活动对象。FlashPlayer从你的应用程序根对象开始(ActionScript3.0中简称为root)直到程序中的每一个引用,都为引用的对象做标记。 接下来,FlashPlayer遍历所有标记过的对象。它将按照该特性递归整个对象树。并将从一个活动对象开始能到达的一切都标记。该过程结束后,FlashPlayer可以安全的假设:所有内存中没有被标记的对象不再有任何活动引用,因此可以被安全的删除。图1 演示了它如何工作:绿色引用(箭头)曾被FlashPlayer 标记过程中经过,绿色对象被标记过,白色对象将被回收。

       
       (3)AS3的内存机制的特点:
          1.  自动内存回收时间不确定。
          2.  当一个对象存在被其他对象引用时,这个对象不会被内存回收。
          3.  当一个流对象被加载,这个被加载的对象及已经占用了内存。
          4.  当一个可视化对象被声明,但没有添加到画面是占用部分内存,加到displayObject上后,占用全部该对象对象全部内存。
         5.  当加载重复对象,例如 加载100个同样的 XX.swf ,如果仅是加载,完成后没有引用,那么内存变化规律,波浪型的。如果某个时间内存回收。那么最后留在内存中的应该是大小近似于加载1个 XX.swf (比1个XX.swf 要大些),从此可以推理出,要是不同的东西被加载,那么最后即便是没有内存漏洞,在一定条件下常用的东西内存中可能也会至少保存每一个不同的东西。经我测试好像是这样的。(测多了可能还会有新发现呢)
         6.  引用的包括
         1)  对对象的存储: 例如 使用一个数组保存 某些对象,那么数组不释放,对象不可能释放
         2)  对事件的监听: 例如 监听过程实际上是使用一个对象保存关键字和关键字关联的事件,对事件关键字,查找然后找出对应的关联function。以下是as2代码。
         3)  强制回收方式,自动内存回收时间不确定,使用特殊的方法,该方法实际上触发一个错误引起资源回收,使无用的不被计数器引用的都要被回收。(暂时不被使用的,没有引用的那个被自动回收保留的那个一个回收掉),附件为强制回收类。
 
         7.  编写代码注意:
         1)  无用的对象,没有引用
         2)  降低类设计之间的耦合度,注意对象传递引用的设计等
         3)  单例模式,在合适的时候使用
         4)  事件循环嵌套造成多次执行,或事件触发循环bug。
         5)  对象重复加同样的监听
       
        (4)AS3开发需要注意的地方:
         1. 被删除对象在外部的所有引用一定要被删除干净才能被系统当成垃圾回收处理掉;
         2. 父对象内部的子对象被外部其他对象引用了,会导致此子对象不会被删除,子对象不会被删除又会导致了父对象不会被删除;
         3. 如果一个对象中引用了外部对象,当自己被删除或者不需要使用此引用对象时,一定要记得把此对象的引用设置为null;
        4. 本对象删除不了的原因不一定是自己被引用了,也有可能是自己的孩子被外部引用了,孩子删不掉导致父亲也删不掉;
        5. 除了引用需要删除外,系统组件或者全局工具、管理类如果提供了卸载方法的就一定要调用删除内部对象,否则有可能会造成内存泄露和性能损失;
        6. 父对象立刻被删除了不代表子对象就会被删除或立刻被删除,可能会在后期被系统自动删除或第二次移除操作时被删除;
        7. 如果父对象remove了子对象后没有清除对子对象的引用,子对象一样是不能被删除的,父对象也不能被删除;
        8. 注册的事件如果没有被移除不影响自定义的强行回收机制,但有可能会影响正常的回收机制,所以最好是做到注册的事件监听器都要记得移除干净。
        9. 父对象被删除了不代表其余子对象都删除了,找到一种状态的泄露代码不等于其他状态就没有泄露了,要各模块各状态逐个进行测试分析,直到测试任何状态下都能删除整个对象为止。
       
       (5)内存泄露举例:
         1. 引用泄露:对子对象的引用,外部对本对象或子对象的引用都需要置null;
         2. 系统类泄露:使用了系统类而忘记做删除操作了,如BindingUtils.bindSetter(),ChangeWatcher.watch()函数时候完毕后需要调用ChangeWatcher.unwatch()函数来清除引用 ,否则使用此函数的对象将不会被删除; 类似的还有MUSIC,VIDEO,IMAGE,TIMER,EVENT,BINDING等。
        3. 效果泄露:当对组件应用效果Effect的时候,当本对象本删除时需要把本对象和子对象上的Effect动画停止掉,然后把Effect的target对象置null; 如果不停止掉动画直接把 Effect置null将不能正常移除对象。
       4. SWF泄露:要完全删除一个SWF要调用它的unload()方法并且把对象置null;
       5. 图片泄露:当Image对象使用完毕后要把source置null;(为测试);
       6. 声音、视频泄露: 当不需要一个音乐或视频是需要停止音乐,删除对象,引用置null;
 

 注意:.在AS3中,delete只能删除类实例中的动态创建的属性, 但是不能删除类成员(类中变量和方法),在AS1和AS2中,delete可以操作任何东西.AS3缩小了范围,只充许操作动态变量

 


附件为强制回收类。调用方法:
import MyGc;
MyGc.GC()
强制回收类

点击下载此文件 

 

 

===========================

 

 

flash as3.0 delete关键词和类成员
2009-06-26 08:27

flash中delete关键词用来移除定义的变量,并不能从内存删除,(从内存删除由”垃圾收集器”来完成),delete只是除去创建的变量,这样别的对象无法访问到它,也无法通过for…in等循环来显示变量.


实质上,Garbage Collector,简称GC,当对象没有任何引用时,GC就会从内存中删除此对象.比如,有两个变量A和B,它们都是引用ObjectX,删除A,GC并不会从内存中删除ObjectX,因为此时它还有B这个引用,如果删除了A和B,ObjectX就没有了引用,GC就会认为它应该从内存中删了.

var a:Object = new Object();
var b:Object = a; // reference same new Object();
delete a;
trace(b); // [object Object] - still exists in memory
delete b;
// GC will mark object for deletion from memory

实际上,在AS1,AS2和AS3中上述工作原理是基本相同的,只是在flash 8中GC有了一点变化,(注意:用GC从内存中删除并不是直接发生的).
尽管有AS3中,有了新的虚拟机来运行脚本,但GC没有太大的实质的变化 ,而delete操作倒是有了些变化.在AS3中,delete只能删除类实例中的动态创建的属性, 但是不能删除类成员(类中变量和方法),在AS1和AS2中,delete可以操作任何东西.AS3缩小了范围,只充许操作动态变量.


ActionScript Code:

// ActionScript 2
class DeleteVarClass {
public var myVar:Number;
function DeleteVarClass() {
myVar = 1;
trace(myVar); // 1
delete myVar;
trace(myVar); // undefined
}
}
ActionScript Code:
// ActionScript 3
package {
public class DeleteVarClass {
public var myVar:Number;
public function DeleteVarClass() {
myVar = 1;
trace(myVar); // 1
delete myVar;
trace(myVar); // 1
}
}
}

上面例子中变量myVar是类定义的一部分,所以在AS3中不能用detele删除它.AS3中如果要让一个类成员变量在内存中不再引用某个对象或者某个值,可以把这个变量值设为null来删除它.
ActionScript Code: myVar = null;
如果对象的所有引用全为null,GC就会标记它为要删除的对象,最后会从内存中删除这个对象.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值