当前位置: > > > Flex4 - 内存泄露原因,以及解决办法

Flex4 - 内存泄露原因,以及解决办法

通过隐式方式建立的对象之间的引用关系更容易被程序员所忽略,从而导致内存泄露。
(1)最常见的以隐式方式建立对象之间的引用就是“绑定”和“为对象添加事件监听器”。
(2)通过测试我们发现“绑定”不会造成内存泄露,对象可以放心地绑定全局对象。
(3)而调用addEventListener()方法“为对象添加事件监听器”则可能产生内存泄露,大多数内存泄露都因此而来:
下面代码:
a.addEventListener(Event.EVENT_TYPE,b.listenerFunction)
使得a对象引用了b对象,如果a对象是一个全局对象(全局对象在应用运行期间始终存在),则b对象永远不会被垃圾回收,可能会造成内存泄露。
比如下面的代码就有造成内存泄露的可能:
this.stage.addEventListener(Event.RESIZE,onResize);
上面代码中的stage是UIComponent的stage属性,表示当前Flex应用运行的“舞台”。
(4)不过,通过以下三种方式使用addEventListener方法不会造成内存泄露:
1.用弱引用方式注册监听器。就是调用时将addEventListener的第五个参数置为true,例如:
someObject.addEventListener(MouseClick.CLICK, otherObject.handlerFunction, false, 0, true);
2.自引用的方式。即:为对象添加的监听处理函数是对象本身的方法。例如:
this.addEventListener(MouseClick.CLICK, this. handlerFunction);
3.子对象引用。即:为子对象添加的监听处理函数是父上对象的方法。例如:
private var childObject:UIComponent = new UIComponent;
addChild(childObject); 
childObject.addEventListener(MouseEvent.CLICK, this.clickHandler);

强引用 和 弱引用
A.addEventListener(event,B.Handler); //强引用
A.addEventListener(event,B.Handler, false, 0, true);  //弱引用
(1)强引用下:listener的生命周期同A。如果A存在,即使B=null,那么listener仍然存在,B也就还在.
但如果A=B,或A是B里面的子对象。那么B=null,listener也就回收了。因为A也就不存在了.
(2)弱引用下:listener的生命周期同B。如果B=null,那么对应的listener也会移除 

Timer一定要使用弱引用
Timer一定要使用弱引用,flashplay认为timer是整个生命周期的。
下面即使是把Listen对象=null,timer也不会被回收,Listen对象也就仍然存在
public class Listen
{
	public function Listen()
	{
		var timer:Timer = new Timer(2000);
		timer.addEventListener(TimerEvent.TIMER,timerHandle);
		timer.start();
	}
	
	private function timerHandle(e:TimerEvent):void{
		Alert.show("handler");
	}
}

内存释放优化原则:
1. 被删除对象在外部的所有引用一定要被删除干净才能被系统当成垃圾回收处理掉;
2. 父对象内部的子对象被外部其他对象引用了,会导致此子对象不会被删除,子对象不会被删除又会导致了父对象不会被删除;
3. 如果一个对象中引用了外部对象,当自己被删除或者不需要使用此引用对象时,一定要记得把此对象的引用设置为null; 
4. 本对象删除不了的原因不一定是自己被引用了,也有可能是自己的孩子被外部引用了,孩子删不掉导致父亲也删不掉;
5. 除了引用需要删除外,系统组件或者全局工具、管理类如果提供了卸载方法的就一定要调用删除内部对象,否则有可能会造成内存泄露和性能损失;
6. 父对象立刻被删除了不代表子对象就会被删除或立刻被删除,可能会在后期被系统自动删除或第二次移除操作时被删除;
7. 如果父对象remove 了子对象后没有清除对子对象的引用,子对象一样是不能被删除的,父对象也不能被删除;
8. 注册的事件如果没有被移除不影响自定义的强行回收机制,但有可能会影响正常的回收机制,所以最好是做到注册的事件监听器都要记得移除干净。
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 还是组件的都需要删除),删除监听器,调用系统类的清除方法)
先remove 再置null, 确保被remove 或者removeAll 后的对象在外部的引用全部释放干净; 
2. 利用Flex 的性能优化工具Profile 来对项目进程进行监控,可知道历史创建过哪些对象,目前有哪些对象没有被删除,创建的数量,占用的内存比例和用量,创建过程等信息
评论0