为了正常的体验网站,请在浏览器设置里面开启Javascript功能!

AS3 内存回收机制

2019-01-14 28页 doc 57KB 8阅读

用户头像

is_614050

暂无简介

举报
AS3 内存回收机制AS3 内存回收机制 一、AS3 内存回收机制    1 二、AS3 内存回收机制    2 三、AS3强制内存回收方法之二    3 四、AS3 释放内存    6 五、AS3中的内存泄露与垃圾回收    8 六、AS3 内存释放优化原则    9 七、as3 内存回收机制测试    10 八、AS3内存优化13条    12 九、AS3的垃圾回收机制详解    14 十、flash as3 内存管理和资源管理    17 十一、轻松几行让你AS3程序不再“内存泄露    18 注意:本资源多半来源于互联网  sword收...
AS3 内存回收机制
AS3 内存回收机制 一、AS3 内存回收机制    1 二、AS3 内存回收机制    2 三、AS3强制内存回收方法之二    3 四、AS3 释放内存    6 五、AS3中的内存泄露与垃圾回收    8 六、AS3 内存释放优化原则    9 七、as3 内存回收机制测试    10 八、AS3内存优化13条    12 九、AS3的垃圾回收机制详解    14 十、flash as3 内存管理和资源管理    17 十一、轻松几行让你AS3程序不再“内存泄露    18 注意:本资源多半来源于互联网  sword收集 一、AS3 内存回收机制 1.什么是垃圾回收器 垃圾收集器是一个后台进程它负责回收程序中不再使用的对象占用的内存。非活动对象就是不再有任何其他活动对象引用它。为便于理解这个概念,有一点非常重要,就是要意识到除了非原生类型(Boolean, String, Number, uint, int除外),你总是通过一个句柄访问对象,而非对象本身。当你删除一个变量其实就是删除一个引用,而非对象本身。 2.as中内存机制的方法 首先是引用计数法: 引用计数法是一种用于跟踪活动对象的较为简单的方法,当你创建一个指向某个对象的引用,该对象的引用计数器加1;当你删除该对象的一个引用,该计数器减1.当某对象的计数器变成0,该对象将被标记以便垃圾回收器回收。 var a:MovlieClip = new MovieClip(); addChild(a); // a的引用计数器为1 var b:MovieClip = a;//a的引用计数器为2 addChild(b); removeChild(a); a = null;//a的引用计数器为1 removeChild(b); b = null; //a的引用计数器为0 其次是标志清除法: 清除法查找非活动对象。FlashPlayer从你的应用程序根对象开始(ActionScript3.0中简称为root)直到程序中的每一个引用,都为引用的对象做标记。 接下来,FlashPlayer遍历所有标记过的对象。它将按照该特性递归整个对象树。并将从一个活动对象开始能到达的一切都标记。该过程结束后,FlashPlayer可以安全的假设:所有内存中没有被标记的对象不再有任何活动引用,因此可以被安全的删除。下图就是那张十分有名的图了: 绿色引用(箭头)曾被FlashPlayer 标记过程中经过,绿色对象被标记过,白色对象将被回收。 最后就是几种比较常见的内存泄露情况: 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()/unloadAndStop()方法并且把对象置null;  5. 图片泄露:当Image对象使用完毕后要把source置null,加载图片的时候最好draw一个出来,然后将loader处理掉。  6. 声音、视频泄露: 当不需要一个音乐或视频是需要停止音乐,删除对象,引用置null; 二、AS3 内存回收机制 AS3通过垃圾回收(Garbage Collection)移除不需要的对象,它需要一定条件。 AS3中的对象被回收需要达到一定条件: 1、  该对象无法通过任何变量访问。也就是没有变量指向该对象,或者指向该对象的变量生命周期结束。 2、  程序中所有对象所占的内存接近当前系统分配给Flash运行的内存容量时,触发回收机制,移除符合条件1的对象。 这里的第二个条件我们无法控制,没有办法手工强制执行垃圾回收,所有不用的对象都是间接移除,也就是不可能即刻移除。这和ActionScript的Increment Mark And Sweep移除策略有关。不再详细讨论。 那么我们要做的工作就是满足第一个条件,让对象符合被回收的条件。 一下如何来满足条件: 1、移除该对象的所有引用,也就是所有引用该对象的变量赋值为null,或刻意的限制该变量的生命周期。 2、对于显示对象或某个数组中的元素,应该在显示列表或该数组中将其移除,再赋值null。 3、该对象注册的所有事件侦听应该被移除,否则该事件依然运行(内存中还在)。如果无法直接删除事件,那么注册侦听的时候应设置为弱引用。 4、暂停该对象的时间线(MC)动画。 5、停止该对象内创建的定时器和移除相关的时间间隔事件。 6、停止该对象中的任何流操作,比如声音流等。 最简单的比如加载图片的Loader的对象,加载完成一定要remove掉侦听,释放时清除对加载内容的引用,调用unload函数清空LoadInfo,再把loader清除出显示列表,赋值为null。 还有当我们在加载另一个swf时,移除loader的同时,不要忘记暂停时间动画,关闭该swf中的流,否则内存中依然存在的的该对象 尽最大可能的重用对象,这样效率要高,而不是让对象老是生存和死亡 三、AS3强制内存回收方法之二 以前我们最熟悉的强制内存回收的方法是: 1.function GC():void{ 2.                try{ 3.                (new LocalConnection).connect("foo"); 4.                (new LocalConnection).connect("foo");}catch(e){ 5.                        trace(System.totalMemory); 6.                        } 7.                } 复制代码 //由于上次试验弱引用的时候,发现当轮询本地sharedobject对象的时候,导致弱引用的内容进行了回收,于是联想用来进行强制内存回收..代码如下: 1.package { 2.        /** 3.         * @author CYPL 4.         * @date 2009-04-01 5.         */ 6.        import flash.net.SharedObject; 7.        import flash.system.System; 8.        import flash.utils.clearInterval; 9.        import flash.utils.setInterval; 10. 11.        public class GCPlus { 12.                public static function clear(isTraceTM : Boolean = false) : void { 13.                        var time : int = 2; 14.                        var interval : int = setInterval(loop, 50); 15.                        function loop() : void { 16.                                if(!(time--)) { 17.                                        isTraceTM && trace(System.totalMemory); 18.                                        clearInterval(interval); 19.                                        return; 20.                                } 21.                                SharedObject.getLocal("cypl", "/"); 22.                        } 23.                } 24.        } 25.} 复制代码 //测试文档类: 1.package { 2.        import flash.display.MovieClip; 3.        import flash.display.Sprite; 4.        import flash.events.MouseEvent; 5.        import flash.system.System; 6. 7.        /** 8.         * @author CYPL 9.         */ 10.        public class GC_Test extends Sprite { 11.                private static const NUM : int = 1000; 12.                private var num : int; 13. 14.                public function GC_Test() { 15.                        init(); 16.                } 17. 18.                private function init() : void { 19.                        num = NUM; 20.                        stage.addEventListener(MouseEvent.CLICK, clickHandler); 21.                        while (num--) { 22.                                var mc : MovieClip = new MovieClip; 23.                                mc.graphics.beginFill(0); 24.                                mc.graphics.drawRect(0, 0, 100, 100); 25.                                mc.x = Math.random() * 500; 26.                                mc.y = Math.random() * 400; 27.                                addChild(mc); 28.                        } 29. 30.                        trace(System.totalMemory); 31.                } 32. 33.                private function clickHandler(e : MouseEvent) : void { 34.                        clear(); 35.                } 36. 37.                private function clear() : void { 38.                        while (numChildren) { 39.                                removeChildAt(0); 40.                        } 41.                        GCPlus.clear(true); 42.                } 43.                /*function GC():void{//这是我们熟悉的方法 44.                try{ 45.                (new LocalConnection).connect("foo"); 46.                (new LocalConnection).connect("foo");}catch(e){ 47.                        trace(System.totalMemory); 48.                        } 49.                }*/ 50.        } 51.} 复制代码 通过System.totalMemory的结果可以清楚的看到内存得以释放,window任务管理器也能够看到有变化哦,原理也可能和异常有点关系. 补充下:也许大家试验其他的异常方法,在IDE调试下也能看到内存释放,但是独立的swf测试的时候,会看到任务管理器中内存并没有得到释放. 目前也只发现传统的LC方法和我上面提到的SO的方法能独立测试也能够达到释放的效果,假如还有新的可能,大伙都来补充下咯 四、AS3 释放内存 在 Flash Player 的发行版中无法直接启动垃圾回收器。要确保将一个对象作为垃圾回收,请删除对该对象的所有引用。请记住,在 ActionScript 1.0 和 2.0 中使用的旧 delete 运算符在 ActionScript 3.0 中有不同的行为。它只能用于删除动态对象的动态属性。 注: 在 Adobe? AIR? 和 Flash Player 的调试版中可以直接调用垃圾回收器。 例如,以下代码将 Sprite 引用设置为 null: var mySprite:Sprite = new Sprite(); // Set the reference to null, so that the garbage collector removes // it from memory mySprite = null; 请记住,当对象设置为 null 时,不必将其从内存中删除。如果系统认为可用内存不是足够低,垃圾回收器可能不会运行。垃圾回收的执行时间不可预知。内存分配(而不是对象删除)会触发垃圾回收。当垃圾回收器运行时,它将查找尚未收集的对象的图形。垃圾回收器通过在这些图形中查找相互引用但应用程序不再使用的对象,从而出处于非活动状态的对象。将删除通过这种方式检测到的处于非活动状态的对象。 在大型应用程序中,此进程会占用大量 CPU 并影响性能,还可导致应用程序运行速度显著降低。通过尽量重复使用对象,尝试限制使用垃圾回收。此外,尽可能地将引用设置为 null,以便垃圾回收器用较少处理时间来查找对象。将垃圾回收看作一项保护措施,并始终尽可能明确地管理对象生存期。 注: 将对显示对象的引用设置为 null 不能确保冻结该对象。该对象在作为垃圾回收之前,仍将占用 CPU 周期。在将对对象的引用设置为 null 之前,先确保正确地停用对象。 可使用 Adobe AIR 和 Flash Player 的调试版中提供的 System.gc() 方法启动垃圾回收器。将此设置与 Adobe? Flash? Builder? 捆绑还可以手动启动垃圾回收器。通过运行垃圾回收器,可以了解应用程序的响应方式以及是否已将对象从内存中正确删除。 注: 如果将某个对象用作事件侦听器,则其他对象可以引用它。如果是这样,先使用 removeEventListener() 方法删除事件侦听器,然后再将引用设置为 null。 幸运的是,这样可以立即减少位图使用的内存量。例如,BitmapData 类包括一个 dispose() 方法。下面的示例将创建一个 1.8 MB 的 BitmapData 实例。当前使用的内存增大到 1.8 MB,System.totalMemory 属性返回一个更小的值: trace(System.totalMemory / 1024); // output: 43100 // Create a BitmapData instance var image:BitmapData = new BitmapData(800, 600); trace(System.totalMemory / 1024); // output: 44964 然后,手动将 BitmapData 从内存中删除并再次检查内存使用情况: trace(System.totalMemory / 1024); // output: 43100 // Create a BitmapData instance var image:BitmapData = new BitmapData(800, 600); trace(System.totalMemory / 1024); // output: 44964 image.dispose(); image = null; trace(System.totalMemory / 1024); // output: 43084 尽管 dispose() 方法可删除内存中的像素,但仍必须将引用设置为 null 才可完全释放内存。当不再需要 BitmapData 对象时,要始终调用 dispose()方法并将引用设置为 null,以便立即释放内存。 注: Flash Player 10.1 和 AIR 1.5.2 在 System 类中引入了一个名为 disposeXML() 的新方法。借助此方法,通过将 XML 树作为参数传递,可立即使 XML 对象可用于垃圾回收。 五、AS3中的内存泄露与垃圾回收 内存泄露与垃圾回收可以说是程序在运行的时候遇到的两个比较重要的问题,这关系到我们程序是否能在机器中正确而有效的运行,你总不希望当你的程序运行一段时间后就弹出“内存******不能为read”的问题吧!很多计算机用户都特别讨厌这个提示。因为只要出现这个错误,程序就会立即关闭(大多数情况下是这样的!)令人烦恼的是,这种错误不可避免。因为一个应用程序少则几千行代码,多则几十万行,甚至上百万行。虽然内存泄露可以通过一定的技术手段排查出来,但是面对这么多的代码,相信再有经验的程序员都会望而却步。所以,我们在编写我们的程序时,尽量避开这些问题。像AS3这样的语言处理内存是贵垃圾回收机管的,我们不能自己去执行清楚内存操作,除非你用的是C或C++。其实现在大部分编程语言都用的是垃圾回收机,在一定程度上给我们的程序编写带来了便利,但同时也会产生问题,比如java,c#,vb等等,这些语言都是使用的垃圾回收机。AS3也是,那么垃圾回收机在什么情况下会产生内存泄露呢? 首先,你要明白什么是内存泄露。在计算机科学中,内存泄漏指由于疏忽或错误造成程序未能释放已经不再使用的内存的情况。内存泄漏并非指内存在物理上的消失,而是应用程序分配某段内存后,由于设计错误,失去了对该段内存的控制,因而造成了内存的浪费。(摘自百度百科)其实,内存泄露很简单,就是我们的程序创建了一个内存快,然后程序继续执行,最后,程序无法在控制这个内存块了。这就好比放羊,我们应该控制我们的羊群去吃草,而不是羊吃完草后,羊就丢了!看似是一个笑话,可这种情况在我们的程序中常常能够见到。知道了内存泄露,你也应该明白我们的程序有效的控制内存是多么的重要了吧!下面我们说说FLASH是如何控制内存的,以及什么时候FLASH对内存失去控制。 垃圾回收机!什么是垃圾?不用的内存不就是垃圾吗?正确!我认为没用的内存都是垃圾!(因为我的机器只有512M内存,内存对我来说太重要了!)清除内存迫在眉睫!但是FLASH不会像你想象的那样勤快的工作。假设我们创建了一个对象。 var a:Object = {name:mebiusshan}; 好了!你能从中看到什么呢?难道你只看到了一个object对象吗?那你的目光就有些短浅了!最少你应该知道,在这个语句中我们创建了一个引用和一个内存块(我不知道内存块用来这里是否合适,暂且这么说吧)引用即a,这是对内存的引用,当有了这个引用之后我们就可以操作这个内存了!太帮了!打一个比方,就好像我们遛狗一样,我们需要一条绳子,套住我们的爱犬!然后你才能放心的去遛狗。好了!当我们创建这样一个对象的时候,FLASH会做什么呢? 先说第一个吧!它的名字叫做“引用计数法”,从名字我们就可以看出来,当一个内存对象添加了一个引用的时候,这个计数器就加1,当删除一个内存对象的引用时,该计数器就减1.当FLASH程序判断,计数器为0的时候,那么表示,这个对象已经么有引用了,没有引用就没办法控制他了,马上删除。这样就OK了!怎么样,这个设计不错吧!想法很好,思路简单。但有一种情况就麻烦了!注意,当有多个对象互相引用的时候,所有的计数器始终为1,这时就出现了内存泄露的情况。该内存确实没用了!但是呢,垃圾回收机却不清理它!郁闷,如果你的机器是512M内存的话。。。。。可想而知,这个办法在一般情况下还是不错的!但是遇到这种情况就OVER了!~那还有第二种方法吗?YES!放心,我们还有第二手准备,它的名字叫“标识清除法”。 这个方法在执行的时候会从FLASH的跟对象也就是root开始到每一个引用的对象做标识。好了!这是第一步,然后FLASH会遍历当前程序中的所有引用,发现没有标识的对象就清除。这样的话清除就准确了!但是会遇到一个问题。遍历是很消耗CPU的,虽然fp9通过调整迭代标识清除缩减对CPU的占用,但是消耗依然很大!所以采用了另外一种比较消极的处理方法!就是分段执行,每次执行一部分,然后偶尔执行一次。这样来减小CPU的消耗。所以我们通常知道,FP9、10的垃圾回收机是偶尔执行的!这也是比较麻烦的一个地方!具体麻烦在什么地方我们下次讨论! 六、AS3 内存释放优化原则 1. 被删除对象在外部的所有引用一定要被删除干净才能被系统当成垃圾回收处理掉; 2. 父对象内部的子对象被外部其他对象引用了,会导致此子对象不会被删除,子对象不会被删除又会导致了父对象不会被删除; 3. 如果一个对象中引用了外部对象,当自己被删除或者不需要使用此引用对象时,一定要记得把此对象的引用设置为null; 4. 本对象删除不了的原因不一定是自己被引用了,也有可能是自己的孩子被外部引用了,孩子删不掉导致父亲也删不掉; 5. 除了引用需要删除外,系统组件或者全局工具、管理类如果提供了卸载方法的就一定要调用删除内部对象,否则有可能会造成内存泄露和性能损失; 6. 父对象立刻被删除了不代表子对象就会被删除或立刻被删除,可能会在后期被系统自动删除或第二次移除操作时被删除; 7. 如果父对象remove了子对象后没有清除对子对象的引用,子对象一样是不能被删除的,父对象也不能被删除; 8. 注册的事件如果没有被移除不影响自定义的强行回收机制,但有可能会影响正常的回收机制,所以最好是做到注册的事件监听器都要记得移除干净。 9. 父对象被删除了不代表其余子对象都删除了,找到一种状态的泄露代码不等于其他状态就没有泄露了,要各模块各状态逐个进行测试,直到测试任何状态下都能删除整个对象为止。 七、as3 内存回收机制测试 通过编写测试程序发现以下规律,flash内存回收机制的一些特点: 1.  自动内存回收时间不确定。 2.  当一个对象存在被其他对象引用时,这个对象不会被内存回收。 3.  当一个流对象被加载,这个被加载的对象及已经占用了内存。 4.  当一个可视化对象被声明,但 没有添加到画面是占用部分内存,加到displayObject上后,占用全部该对象对象全部内存。 5.  当加载重复对象,例如 加载100个同样的 XX.swf ,如果仅是加载,完成后没有引用,那么内存变化规律,波浪型的。如果某个时间内存回收。那么最后留在内存中的应该是大小近似于加载1个 XX.swf (比1个XX.swf 要大些),从此可以推理出,要是不同的东西被加载,那么最后即便是没有内存漏洞,在一定条件下常用的东西内存中可能也会至少保存每一个不同的东西。经我测试好像是这样的。(测多了可能还会有新发现呢) 6.  引用的包括 1)  对对象的存储: 例如 使用一个数组保存 某些对象,那么数组不释放,对象不可能释放 2)  对事件的监听: 例如 监听过程实际上是使用一个对象保存关键字和关键字关联的事件,对事件关键字,查找然后找出对应的关联function。以下是as2代码。As3 的EventDispatcher功能类似 var btnListener:Object = new Object(); btnListener.click = function()  { trace("haha") }; bt1.addEventListener("click", btnListener); 使用removerEventer 方法定是要清除掉 处理关键字索引和function 的对象。这样即清除掉了计数引用。 3)  强制回收方式,自动内存回收时间不确定,使用特殊的方法,该方法实际上触发一个错误引起资源回收,使无用的不被计数器引用的都要被回收。(暂时不被使用的,没有引用的那个被自动回收保留的那个一个回收掉) 方法: try { trace("force a GC"); new LocalConnection().connect('foo'); new LocalConnection().connect('foo'); } catch (e:Error) { trace(e.message); } } 一个例子 假如 有个loader 请求加载url=xxxx.swf 的地址,然后成功加载 xxxx.swf, 10次 ,每次成功后没有处理,假设这时候自动回收没有调用,那么使用强制回收,在debug模式下,会看到回收资源。 [Unload SWF] xxxx.swf ,10个这样的输出。 7.  编写代码注意: 1)  无用的对象,没有引用 2)  降低类设计之间的耦合度,注意对象传递引用的设计等 3)  单例模式,在合适的时候使用 4)  事件循环嵌套造成多次执行,或事件触发循环bug。 5)  对象重复加同样的监听 根据内存特性制作了资源管理类模块 monitor XML 文件 1.客户端根据主配置文件加载 相关模块配置文件,其中之一模块就是monitor xml文件 在编译发布客户端时,可以不包括此文件,在开发人员使用时可以包括此文件,监视客 户端运行情况,那么这样有助于使用更少的类,节省资源。 依赖于XML配置体系的修改,打算客户端重构后实现。 Resource Monitor 资源监视类 资源监视,主要利用弱引用,监视对象是否存在,对象加载的资源大小可以获得,同过对象 是否存在来统计该所有对象资源大小总和。 统计过程中需要对资源类型划分,这里涉及到类型如何划分,以及改造过程中如何传递这 些资源的类型。因为具体改造就是通过一个公用函数获取资源,那么就需要告诉这个函数, 什么地方(模块)用,以便统计。  工作量分布 1编写Resource Monitor类,2修改代码中所有资源加载处。 有一点需要注意,这次添加Resource Monitor类的方式,在以后重构后,加载调用的函数方 式可能都需要改变,设想通过服务来获取资源。那么这个服务的名称等信息自然会得到。 使用方法 weeklyLoader = new Loader();//程序中的loader加载 ,不需要改变 RF.load(weeklyLoader,path,"人物图片");//添加 RF 为资源管理类,采用静态方法访问,path 是资源的url,”人物图片”是模块或资源类型的名称。不需要传递回调方法。 八、AS3内存优化13条 1. 使用合适的显示对象,对于非交互的简单形状用Shape对象,对于不需要时间轴的交互式对象用Sprite,对于使用时间轴的动画用MovieClip,他们的内存使用量分别是236,412,440,可见shape很省内存 (小焱补充:Sprite相当于静态图片,MovieClip相当于动态图片,因为是动态的所以带时间轴,对于始终静止的对象还是建议使用Sprite) 2. Number原始存储内存占8个字节,int,uint,Boolean,String均占4个字节, 关于赋值后作占内存,取决值赋的值 (小焱补充:这个对于任何编程语言都是一样的,不同的变量初始化的时候内存是不一样的,比如你一个100以下的数字声明为int就比声明为number要省4个字节,但是AS和编译型语言不一样,他会根据赋值来自动更改) 3. 对象的重复利用, 在FOR循环内new对象要小心,每次new都会增大内存。 (小焱补充:AS中内存增加是因为new,而不是addChild,而removeChild并没有真正删除一个元件,而是仅仅删除元件的索引,所以内存并不会减少,遗憾的是AS并没有类似Free这样的方法去强制释放内存,只能根据其垃圾回收机制自动释放) 4. 通过重复使用 BitmapData 对象可以节省更多的内存,即不同的bitmap公用一个bitmapdata 5. 重复使用相同对象的时候,可以考虑下对象池的应用 6. 垃圾回收运行时会不断检测出处于非活动状态的对象,大型项目中此进程会占用大量CPU,所以尽量重使用对象,不用的对象设置为NULL (小焱补充:对于removeChild的对象,最好再加上一句 “对象 = NULL”,注意NUll和null是不一样的,NULL表示一个空对象,而null表示一个空字符,空变量等等) 7. 内存释放方面,为了确保被垃圾回收,首先保证该对象没有其他引用,然后移除监听,在然后设置为NULL。关于bitmapdata, 先用dispose 在设置为null (小焱补充:关于addEventListener,他最后一个参数设置为true,表示弱引用,这样即使没用removeEventListener也会在一定时间后被系统垃圾回收机制自动删除,这个时间表示为该侦听器持续没有监听到事件的时间) 8. 在 Flash Player 10.1 中,对所有bitmapdata实例采用同一版本的bitmapdata, 大大节省了内存, 当bitmapdata数据发生变化的时候,内存中会建立一个新的bitmapdata实例 (小焱补充:FlashPlayer 10后续版本做了大量的内存优化) 9. 尽量避免使用滤镜,当对显示对象使用滤镜的时候,内存中将创建两个位图,每个位图的大小都与显示对象相同。一个是显示对象的栅格化版本,一个是用于滤镜的位图 10. 当修改滤镜的属性的时候,内存中两个位图都将更新创建新的位图,会消耗一些CPU,并且会占用大量内存。 11. Flash Player 10.1 在所有平台上引入了一种新的过滤行为。如果滤镜在 30 秒内没有进行修改,或者将其隐藏或置于屏幕之外,将释放未过滤的位图占用的内存。该方式成为动态位图卸载。 12. 使用滤镜时仍要谨慎小心;在对它们进行修改时,仍要求大量 CPU 或 GPU 处理。 13. 如果想要滤镜效果,最好用PHOTOSHOP来做一个 (小焱补充:的确,对于使用代码生成的效果肯定比直接带效果的图片要更加耗费资源,所以,能使用PS处理的效果就不要交给AS代码来做) 九、AS3的垃圾回收机制详解 AS3相对于以前版本的功能增强了很多,在赋予它重任时,同时也要它付出代价:垃圾收集器不再支持自动为你收集垃圾。本文中,我为大家整理了一些资料。首先,我们先来了解下垃圾收集器是个什么东西?     (1)关于垃圾收集器 垃圾收集器是一个后台进程它负责回收程序中不再使用的对象占用的内存。非活动对象就是不再有任何其他活动对象引用它。为便于理解这个概念,有一点非常重要,就是要意识到除了非原生类型(Boolean, String, Number, uint, int除外),你总是通过一个句柄访问对象,而非对象本身。当你删除一个变量其实就是删除一个引用,而非对象本身。 (2)AS3的内存机制的方法: 引用计数法:引用计数法是一种用于跟踪活动对象的较为简单的方法,它从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 引用计数法简单,它不会非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 增加了一个叫做标识-清除的新垃圾回收技术。 标识-清除法 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; 附件为强制回收类。调用方法: import MyGc; MyGc.GC() 强制回收类 原创文章如转载,请注明:转载自FLEX开发乐园 [ ] 另外写一写自己看到的强制垃圾回收方式: 核心代码: try{ new LocalConnection().connect("MoonSpirit"); new LocalConnection().connect("MoonSpirit"); }catch(error : Error){ } 另外关于为什么用LocalConnection方式的解释是 其实LocalConnection与垃圾回收是没有直接关系的. 这个做法的原理在于垃圾回收的时间尽管不确定,但是,只要程序抛出错误,就会运行一次垃圾回收器.这里使用LocalConnection两次connect同一个连接,第二次将发生运行时错误(#1034,LocalConnection已经连接上).于是就报错了.垃圾回收器自动运行. 至于为什么网上流传的版本都是LocalConnection,我个人觉得,可能是因为这个LocalConnection在AS3的应用中相对比较少出现,而且跟其他代码相比,这个运行时错误不容易与其他代码发生冲突. 用try catch就可以让错误静默失败,既屏蔽了错误,又调用了垃圾回收。学习~~ 十、flash as3 内存管理和资源管理 资源管理: 1、资源的回收,当发现没有引用者(unload而且没有相关的事件-such as listener-关联)后,会在下一次GC马上回收 2、因为需要GC下一次回收才能清理,并且没有任何方法卸载掉swf,所以界面不应该同时load入多个swf(对UI的设计有要求) 3、如果和当前的stage产生了关系(如鼠标事件和键盘事件的监听)且该关系不是弱引用的swf,则无法清理,只有退出了stage才能清理 资源回收管理的建议: 1、使用System.totalMemory进行检测(如果全部都是自己开发的swf可用flex builder的profile) 2、使用weak reference来设置listener 十一、轻松几行让你AS3程序不再“内存泄露 最近貌似大家很喜欢“内存回收”这东西 老实说,这东西不管也罢…它有机会就会回收的,强制内存回收也是用来玩玩而已 但是,有一个和内存回收无关的东西--程序设计上导致的内存占用不断上升,这点是靠我们经验来解决,而不是靠那个所谓的内存回收机制。 如果你觉得这文很长,直接看后面… 怎么样编写内存占用少的AS3程序?我经验不足怎么办? 正所谓的,看代码,就是和其他程序员的心灵的交流,所以下面我就无耻地请出我们的代码了。 请大家定位到Flex3InstallRoot\sdks\3.0.0\frameworks\projects\framework\src\mx\managers\PopUpManagerImpl.as中,看看人家究竟是怎么个设计。 大家请先看 public function addPopUp 这个函数 里面一对代码都是关于如何在PopupManager中创建一个弹出窗口的代码,其中有一点很值得重视… // Listen for unload so we know to kill the window (and the modalWindow if modal) // this handles _all_ cleanup window.addEventListener(Event.REMOVED, popupRemovedHandler); 复制代码 就是在这里,PopupManager给添加了个Remove事件的监听器。 跟着,如果我们要去除这个PopUp的时候是调用RemovePopup的,我们看看这个函数是怎么写的: public function removePopUp(popUp:IFlexDisplayObject):void { // all we want to do here is verify that this popup is one of ours // and remove it from the display list; the REMOVED handler will do the rest // (this is so that we never leak memory, popups will self-manage even if //  removePopUp is not called). if (popUp && popUp.parent) { const o:PopUpData = findPopupInfoByOwner(popUp); if (o) { const sm:ISystemManager = ISystemManager(popUp.parent); if (o.topMost) sm.popUpChildren.removeChild(DisplayObject(popUp)); else sm.removeChild(DisplayObject(popUp)); } } } 复制代码 看见开发人员自己写的注释没?他意思是说,当执行removeChild的时候,remove时间会自动触发,来给他们做剩下的清理工作。 跟着我们看看popupRemovedHandler 毫无疑问,它对里面的对象做了一些清理工作,具体就看代码吧...... 这个类告诉我们一个很明显的道理: 任何一个DisplayObject被移除的时候都会发出Remove事件,我们只要对这个remove事件进行监听,再进行相关的清理工作就可以了。
/
本文档为【AS3 内存回收机制】,请使用软件OFFICE或WPS软件打开。作品中的文字与图均可以修改和编辑, 图片更改请在作品中右键图片并更换,文字修改请直接点击文字进行修改,也可以新增和删除文档中的内容。
[版权声明] 本站所有资料为用户分享产生,若发现您的权利被侵害,请联系客服邮件isharekefu@iask.cn,我们尽快处理。 本作品所展示的图片、画像、字体、音乐的版权可能需版权方额外授权,请谨慎使用。 网站提供的党政主题相关内容(国旗、国徽、党徽..)目的在于配合国家政策宣传,仅限个人学习分享使用,禁止用于任何广告和商用目的。

历史搜索

    清空历史搜索