1.被删除对象在外部的所有引用一定要被删除干净才能被系统当成垃圾回收处理掉;
2.父对象内部的子对象被外部其他对象引用了,会导致此子对象不会被删除,子对象不会被删除又会导致了父对象不会被删除;
3.如果一个对象中引用了外部对象,当自己被删除或者不需要使用此引用对象时,一定要记得把此对象的引用设置为null;
4.本对象删除不了的原因不一定是自己被引用了,也有可能是自己的孩子被外部引用了,孩子删不掉导致父亲也删不掉;
5.除了引用需要删除外,系统组件或者全局工具、管理类如果提供了卸载方法的就一定要调用删除内部对象,否则有可能会造成内存泄露和性能损失;
6.父对象立刻被删除了不代表子对象就会被删除或立刻被删除,可能会在后期被系统自动删除或第二次移除操作时被删除;
7.如果父对象remove了子对象后没有清除对子对象的引用,子对象一样是不能被删除的,父对象也不能被删除;
8.注册的事件如果没有被移除不影响自定义的强行回收机制,但有可能会影响正常的回收机制,所以最好是做到注册的事件jian听器都要记得移除干净。
9.父对象被删除了不代表其余子对象都删除了,找到一种状态的泄露代码不等于其他状态就没有泄露了,要各模块各状态逐个进行测试分析,直到测试任何状态下都能删除整个对象为止。
内存泄露举例:
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;
内存泄露解决方法:
1.在组件的REMOVED_FROM_STAGE事件回掉中做垃圾处理操作(移除所有对外引用(不管是VO还是组件的都需要删除),删除jian听器,调用系统类的清除方法)
先remove再置null,确保被remove或者removeAll后的对象在外部的引用全部释放干净;
2.利用Flex的性能优化工具Profile来对项目进程进行监控,可知道历史创建过哪些对象,目前有哪些对象没有被删除,创建的数量,占用的内存比例和用量,创建过程等信息;
总结:关键还是要做好清除工作,自己设置的引用自己要记得删除,自己用过的系统类要记得做好回收处理工作。以上问题解决的好的话不需要自定义强制回收器也有可能被系统正常的自动回收掉。
文章二
对于Flex性能我想是很多真正打算用它的人最关心的问题,也是我用了这么久之后体会比较深刻的问题,
目前我做过的系统性能也是一个大问题,在这里我总结了一些在技术上提高性能的方法。
1.避免容器的深层嵌套
(因为每个容器都会对其子容器进行计算和调整,一般建议HBox,VBox最好 不要超过3层,但对于复杂系统这个很难避免)
2.对于容器和空间的位置和尺寸尽量采用绝对坐标和硬编码。(减少FlashPlayer深层次的嵌套计算)
3.合理恰当的使用Grid容器,对于能用普通容器如HBox,VBox实现的尽量不要采用Grid(Grid嵌套层次深)
4.避免使用重复、冗余的容器嵌套,如:
1)在Panel,Application中添加一层VBox,Panel,Application本来就是继承于VBox的,添加无谓的VBox只能降低你系统的性能。
2)对于自定义的容器减少重复多余的父容器,比如:
<mx:VBoxbackgroundColor="#FFCCCC"borderStyle="solid">
<myComponentxmlns="*"/>
</mx:VBox>
你完全可以采用下面的写法,对于myComponents可能它本身就是VBox
<myComponentxmlns="*"backgroundColor="#FFCCCC"borderStyle="solid"/>
5.延迟实例化导航类容器(DeferredInstantiationNavigatorContainers)
如ViewStack,Accordion,TabNavigator等,本身就提供了延迟实例化的功能,你只需要修改其 creationPolicy='auto'即可,它将只实例化第一个要显示的子控件,其余的将在需要的时候实例化。
6.对于flex的效果(Effects)要做到尽量平滑(Smoothly),比如延长它的持续时间(duration),避免使用bmp格式的图片作背景。
7.对于DataGrid的复杂ItemRenderer,尽量用Canvas而不是VBoxorHBox,这个和Flex1.5中是不同的。
8.如果DataGrid的ItemRenderer是ComboBox等,尽量用ItemEditor而不是ItemRenderer,只有当选中的 cell才会显示其Renderer(避免一次性把所有的cellRenderer都画出来)
9.记得随时Remove没有用的Listener或采用弱引用,这个我在事件机制中提到过(URL...);null不用的变量和数组。
10.对于大批量的数据采用需要的时候去取或者分页
11.提前声明频繁使用的变量,如:
varl:int=list.length;
varw:int=stage.stageWidth;
for(vari:int=0;i<l;i++){
list[i].x=w-100;
}
12.这样也能提高Flex的性能,可能很多人都会不屑,但至少对于Flex来讲它能减少不必要的计算,改善性能,我想这个编程习惯问题要改还真是不容易,至少我写了1年多直到最近才注意到这个问题。
另外和代码组织有关的建议,如:
1)系统性的常量写在一个常量类里头并用const关键字,一些ItemRenderer代码尽量独立成一个文件而不是直接到处都写
2)不要在界面元素中引用一大串的parent.parent.parent...,一旦界面重新组织将给你带来很大麻烦。如果你用Cairngorm框架,那么就应该用ViewHelper,尽量的松耦合你的代码。
13.修改production-mode属性为true,2.01在文件flex-webtier-config.xml中。它一般是产品发布的时候要设置的,你可以理解为c++的Release和Debug版本之间的差异。
14.如果你用FDS,那么尽量用RemoteObject而不是HttpServiceorWebService,因为前者的效率最高
15.对你的系统进行压力测试,Adobe有一个测试工具,叫FlexStressTestingFramework
(rul:http://labs.adobe.com/wiki/index.php/Flex_Stress_Testing_Framework).
它能提供对RPC中的RTMP,AMF,HTTP测试,一般的商业测试软件不支持前两者的测试,
该软件完全免费,你可以到Adobe官网上下
=========================================================================
Flash内存清理、建议以及查找内存泄露的方法(转载)一、Flash内存清理
Flash Player的garbage collection(GC)分两种运行方式,一种是“引用计数法”(Reference Counting),一种是“标记-清除法”(Mark Sweeping)。
1>、引用计数法是通过计算指向某个对象的引用的数量来确定是否清除该对象。如果一个对象的引用数量为0,表示程序无法再访问到该对象,则清除该对象;如果引用计数不为0,则不清除。这种方法运行代价较小,但是这种方法无法清除存在循环引用关系的对象集合。
2>、标记-清除法是从程序的根对象开始,遍历每个引用指向的对象。遍历经过的对象,则将其标记。最后清除所有没有打上标记的对象。这种方法比较彻底,但是运行代价较高。
FlashPlayer运行GC的时间并不固定,它会根据你的内存的占用情况来决定运行GC的时间。它会根据用户机器的内存值来设定一 个阀值,然后将程序的占用内存量保存在该阀值左右。 正因为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. 使用实例成员(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).方法如下:
![](https://i-blog.csdnimg.cn/blog_migrate/81178cc93a2a3bb5048d90d76e7ec935.gif)
{
import flash.net.LocalConnection;
var conn1:LocalConnection = new LocalConnection();
var conn2:LocalConnection = new LocalConnection();
conn1.connect( " gc " );
conn2.connect( " gc " );
}
catch (e:Error)
{
}
8. 使用release版的module swf.
9. 卸载debug版的flash player.
10. 安装release版的flash player.
三、关于查找内存泄漏
加载/卸载多个SWF模块和子应用是涉及内存泄漏最常见场景。每天,我们了解到播放器更多的如何进行内存管理及其特性,因此该总结了。
当调试怀疑在加载/卸载SWF时发生了内存泄漏,我一般会执行以下操作:
- 1)建立应用程序或测试来多次加载和卸载SWF(至少3次),迫使垃圾收集器在每次加载或卸载后工作,接着使用性能分析工具来查看内存中有多少个模块的xxx_FlexModuleFactory或子应用程序的SystemManager的副本。如果超过1个,保持加载和卸载查看该数值是否继续增长。任何模块或SWF引入不同风格的新组件将 需要利用StyleManager注册,StyleManager始终首先被加载。你可以在主程序中或通过CSS模块预加载样式来防止其发生。如果加载第 二个副本可能在此处停留,因为播放器或FocusManager可能一直挂起,如果你看到超过2,绝对是内存泄漏了,您应该使用Profiler来寻找泄 漏。
- 2)经过多次装卸后,采用内存快照,然后再多次加载和卸载再采用快照。清除所有的过滤器,删除百分比,用类名排序并手动比较每个类的实例数量。它们可能是完全匹配地,除了少数字符,弱引用。一切都值得怀疑和调查。
- 3) 我想清理SWF得到所有引用,接下来在调试器中多次加载和卸载并查看console面板信息。在调试面板中查找以[UnloadSWF]标记开始的 行: 告诉我播放器认为SWF卸载后一切都清理了,请注意,清理可能不是马上就执行的,即使有时播放器请求GC(垃圾回收)后若SWF还有内部引用会得 到“later”稍后清理。如果不明白,回到第2步比较内存快照查找可能泄漏的对象。
- 4) 现在我确信即使播放器在卸载SWF后认为一切OK了,但System.totalMemory却仍在增加,最后的测试将其导出为release版并运行在release播放器中。调试版播放器会将调试信息编译进SWF中,这样会歪斜System.totalMemory的值(值会不正确)。目前测试,一旦通过第3步,release播放器的System.totalMemory报告是可以接受的,一个小得多的并可接受的最大内存值上限。
- 5) 通过上述操作后,当使用操作系统工具检查播放器进程,你可能会发现播放器内存属性仍然在增长,这个问题是播放器团队留下的研究空白区域。对于 Internet Explorer,人们常发现最小化IE会导致内存应用减小,这竟味着IE的内存管理器在处理着什么,而不是播放器或你的应用程序减小了内存。我们不知道 有什么编程方式可以强迫IE缩减内存。即使Flash认为所有对象应该已被卸载我们也要看看其它浏览器的内存增长报告
===================================================================================
Profiler(概要分析-----性能分析)
刚做完一个程序,想用flash profiler做一下性能调试,可是不管我怎么点pro可是我照做了,还是没能调出该功能。后来去了国外网站,大概看了一下,可能是防火墙通过9999端口阻止了Socket通讯。然后建议再修改一下端口号。以下是我的完整做法,希望给大家一个参考
1.通过Window--Preferences--FlashBuilder--Profiler--Connections--Port Number:改为8888
2.在C:\Documents and Settings\〔计算机用户名〕\文件夹下,找到MM.cfg在头上添加了PreloadSwf=<workspace> \.metadata\.plugins/com.adobe.Flash.profiler/ProfilerAgent.swf?host=localhost&port=8888
ProfilingFileOutputEnable=1
PolicyFileLog=1
PolicyFileLogAppend=0
ErrorReportingEnable=1
TraceOutputFileEnable=1
两句,这时就可以打开Profiler功能了。注意:打开的时候有可能会弹出一个对话框,上面写着:If the tabbed browsing feature in your browser is enabled and a browser is currently opened profiling may not start proerly .please close your browser before continuing.
刚开始我以为是自己运行时碰到了错误,后来仔细翻译了一下,才知道,这只是Flex builder的一个善意提醒,大概意思是:如果您的浏览器有选项卡功能并且您已经打开了一个浏览器,此时Profiler功能有可能打不开。请在继续打开该功能之前关闭所有浏览器窗口。
不用管它,点击OK就可以了
===========================================================================================
Flash Builder概要分析不能用之解决一直被我的Flash Builder的概要分析不能使用的问题所困扰,baidu google都无解。
无法连接到应用程序以访存概要分析数据。请再次尝试对应用程序进行概要分析。
如图:
后来一个同事让我设置下我的系统用户文件夹,他说他以前也不行,因为他的系统不再c盘,所以要人为制定。
进入Flash Builder安装目录,打开FlashBuilder.ini文件,增加下面这句话:
1 | -Duser.home=C:\\Users\\xxxxxxx |
这里的xxx是你的系统账户名,win7下是users,xp下是document and setting(如:-Duser.home=C:\\Users\\Documents and Settings)。
如此接解决了!
但是很奇怪,按理来说我的系统账户在c盘的,自己能找到的,为什么不找。安装flash builder的时候,自己不会加入这句么?
使用admin账户的都没这个问题。
还有概要分析的时候,如果我用的flash player 独立播放器调试,使用强制内存回收,总有些实例不能回收。用浏览器才行,奇怪!
====================================================
凡事应以“预防为主”,预防内存泄露要注意的地方:
- 对于显式引用,要尽量减少对临时对象的引用,尤其是全局变量、静态变量、使用单例模式创建的变量对临时变量的引用。这些变量包含stage、 systemManager、application、MVC框架中Model和Controller,还有以Manager命名的对象等等。另外,临时 变量本身要尽量做到高内聚性,对象内部尽量减少对外部对象尤其是全局对象的依赖。
- 对于隐式引用,使用弱引用方式注册事件监听器,将最后一个参数useWeakReference设置为 true:a.addEventListener("Leak", b.leakHandler, false, 0, true); 这样做的结果是垃圾回收器在做标记时,会忽略a对于b的引用,如果b没有被其他对象引用,垃圾回收器就把它标记为“无效”进而回收,从而避免内存泄露。