FLEX内存相关

 虚拟机(VM)

flashplayer是基于一个虚拟机(精确的来说是2两个,一个是为actionscript2的一个是为actionscript3的),当你需要创建新的对象时虚拟机动态分配内存,例如下面的代码来创建一个新的对象:

  var o:Object = new Object(); 
在启动时VM事先占用(reserve)了一些内存,当上面的代码被执行时VM决定那个对象放在应用内存的什么位置以及它需要占用多少空间。当你创建对象时,VM可能使用启动时占用的所有内存,如果需要,再向操作系统请求更多的内存。除非你有一个程序仅仅是毫无疑义的创建新对象,正常的应用在执行时必然会有一个对象变为无用的时候,例如,为了正确的执行程序,它不需要存在了。你将会看到,理解一个对象何时不再被需要是不容易的事情。就现在,让我们加深我们能够检测到它。在Flash VM架构,你不能明确的说“删除它”,内存使用是被VM本身所管理的,VM本身负责检查哪些对象是无用的并擅长他们,这样的机制就叫做垃圾回收。

在启动阶段应用占用了一些要使用的内存,比如说4个块。当你创建了一个对象,VM将第一个空位分配给它。我们说,过一会当o1不再需要是你将它设为null。你创建一个新的对象o2,你希望o2代替o1。有时它发生,有时它不会发生,这取决于垃圾回收机制,这样一种非常复杂的过程,我们不在这里描述。这时我们已经得到一个教训:“设置一个对象为null不一定能够释放它占用的内存”。这取决于flash中已经实现的垃圾回收的方式,GC由重分配触发而不是由删除触发,这意味着GC周期在你声明new Object()的时候运行而不是你设置它为null时运行。

内存消耗

如果你不得不使用AS3和Flex,你可能知道你可以动态的添加UI元素到图形界面中,通过一个简单的方法叫做addChild(),相反的方法是removeChild(),它用来移除一个显示元素从UI中。更精确的来说,元素是从视图中删除,但是这不意味着它已经被垃圾回收了。让我来介绍一个简单的场景给你展示很容易相信removeChild()的事实。

许多Flex应用从服务端加载数据并根据返回的值动态展示它,通常情况视图代码被隔离到一个组件中,经常被称为renderer,它负责展示来自服务器的数据。我没设计一个非常简单的renderer,它由两个文本字段,嵌入一个VBox中,数据显示是field1和field2,被提供的对象的属性作为值来源:

<mx:VBox xmlns:mx="http://www.adobe.com/2006/mxml" 
    borderStyle="solid" width="200" 
    >
    <mx:Script>
        <![CDATA[
	    [Bindable] private var _field1:String;
	    [Bindable] private var _field2:String;
 
	    override public function set data(value:Object):void {
		_field1 = value.field1;
		_field2 = value.field2;
	    }
	]]>
	</mx:Script>
 
	<mx:Text text="{_field1}" />
	<mx:Text text="{_field2}" />
</mx:VBox>

让我们通过一个简单的函数来模拟数据载入,它返回对象的一个数组:

private function getData():Array {
    var a:Array = new Array();
 
    for (var i:uint = 0; i< 200; i++) {
	var o:Object = new Object();
	o.field1 = "field "+Math.random().toString();
	o.field2 = "field "+Math.random().toString();
	a.push(o);
    }			
    return a;
}

为了渲染数据,我们使用一个简单的函数,来创建一个renderer,设置它的data属性,并添加到VBox中:

private function loadData():void {
    vbox.removeAllChildren();
    var array:Array = getData();
 
    for (var i:int = 0; i < array.length; i++) {
	var rend:MyRenderer = new MyRenderer();
	rend.data = array[i];
	box.addChild(rend);
        i++;
    } 
}

你看到那个危险的声明了吗?还没有?让我来显示给你,为了模拟一个重复的操作,我添加了一个timer,它会每N秒都调用加载数据的函数:

private function init():void {
    var t:Timer = new Timer(2000);
    t.addEventListener(TimerEvent.TIMER, tick);
    t.start();
}

现在,试着运行一下profiler(笔者注:flexbuilder3中的工具),你看到像下图一样一直增长的图形了吗?

恭喜,我们已经发现了一个内存泄漏!内存泄漏发生在重复的动作并且内存消耗持续增长而不是保持恒定。你发现了那个危险的声明了吗?它就是当你创建renderer是发生的。为什么?因为你认为removeAllChildren()从内存中移除了那些renderers。错误!正像上面所说,这个方法仅仅从显示树(显示列表)中移除了renderers,事实上,正如你从profiler中看到的,renderers还在那儿,继续消耗着内存。

技术上讲这不是一个内存泄漏,因为这没有任何东西阻止垃圾回收器去清除来自renderers的内存。事实上这种情形下,内存泄漏发生在,当有些对象被认为使用了renderer(例如一个listener),即使当你从显示树中移除了它。在这个例子中renderers是“自由的”,可能被垃圾回收,但是,他们不是这样的,因此,结果仍然与内存泄漏是相同的,不断增长的内存消耗:

有许多技术来解决这种情况,我们来展示两个:缓存renderers和动态缓存。

缓存

让我们假设,你实现知道需要多少renderers,一个方法来解决内存泄漏就是在启动是来创建一个renderers的缓存:

private var cache:Array = new Array();
 
private function initRenderers():void {
    for (var i:int = 0; i < 200; i++) {
	renderers.push(new MyRenderer());
    }
}

我们于是可以这样修改我们的loadData方法:

private function loadData():void {
    container.removeAllChildren();
    var array:Array = getData();
 
    for (var i:int = 0; i < array.length; i++) {
	var rend:MyRenderer = cache[i];
	rend.data = array[i];
  	container.addChild(rend);
	i++;
    } 
}

正如你所见,我们不再创建新的renderers,而是在缓存中查找一个。

很多情况下,你事先不知道多少数据从服务器返回,而且不知道你需要多少renderers,这时你需要一个动态缓存。

动态缓存

动态缓存是基于一个弹性的机制,你有一个地方可以来查找一个renderer:如果缓存中有一个,那就返回它,否则一个新的被临时创建,最好来看看下面的代码:

public class MyRendererCache extends Object {
 
    private static var cache : ArrayCollection = new ArrayCollection();
 
	public function MyRendererCache() {
	    super();
            for ( var x : Number = 0; x < 20; x ++ ) {
		cache.addItem( new MyRenderer() );
	    }
	}
 
	public static function getRenderer() : MyRenderer {
	    var renderer : MyRenderer;
 
	    if ( cache.length <= 0 ) {
		renderer = new MyRenderer();
	    } else { 
		renderer = cache.removeItemAt( 0 ) as MyRenderer;
	    }
 
	    return renderer;
        }
 
        public static function setRenderer( renderer : MyRenderer ) : void {
	    cache.addItem( renderer );
        }		
}

在构造函数中,你操作缓存用最小数目的renderers,比如说2个。这个缓存有两个静态方法,getRenderer和setRenderer,第一个是来获得一个renderer,第二个是当结束时返还给它。这种方式下内存中renderers的数量可能增长超过最小的数目,但是仅是临时的,因为GC会在他们当不在被引用时删除他们。一个重要的问题是跟setRenderer有关,当你不再需要一个renderer时,你必须把它返回给cache,否则我们又陷入内存泄漏中,如上解释的那样。为了达到这个目的,我们来利用renderer的remove事件,remove事件是当一个UI元素被从显示列表中移除时被触发。例如当我们调用removeAllChildren(),这样的事件对于每一个renderer来说都会被触发。我们可以这样修改renderer:

<mx:VBox xmlns:mx="http://www.adobe.com/2006/mxml" 
    borderStyle="solid" width="200" 
    remove="onRemove()"
    >
 
    private function onRemove():void {
	MyRendererCache.setRenderer(this);
    }
....
</VBox>


如果现在来运行profiler,你会注意到,内存增长到一个给定的点并且保持稳定,如下图所示:

恭喜,你已经解决了内存泄漏!

建议性GC 

除了偏向于自动的GC过程,adobe还允许程序员来建议干涉性的GC。这个命令在flash.system包中的System.gc(),通过这篇文章“强制垃圾回收过程”,但是在我的经验来看,这仅仅是一个模糊的干涉建议,它能解决某些情况,因此在开始时它值得一试,当你需要一个快速的方法来节省一些内存时。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Flex中,内存回收主要依赖于垃圾回收器(Garbage Collector)自动处理。垃圾回收器会自动检测和释放不再使用的内存,以避免内存泄漏和提高应用程序的性能。 以下是一些在Flex中帮助垃圾回收器进行内存回收的常见方法: 1. 解除引用(Release References):确保不再使用的对象没有被其他对象引用。当对象不再被引用时,垃圾回收器会自动将其标记为可回收,并在下一次垃圾回收时释放内存。 2. 删除事件监听器(Remove Event Listeners):如果对象注册了事件监听器(Event Listener),在不再需要该对象时,务必记得手动删除对应的事件监听器。否则,即使对象本身没有被引用,由于事件监听器的存在,垃圾回收器可能无法释放相关内存。 3. 关闭和释放资源(Close and Release Resources):在使用文件、网络连接等资源时,确保在不再需要时及时关闭和释放这些资源。避免资源的泄漏和占用过多的内存。 4. 避免循环引用(Avoid Circular References):循环引用指的是对象之间相互引用,导致它们无法被垃圾回收器释放。尽量避免出现循环引用的情况,或者在不需要时手动断开循环引用。 需要注意的是,Flex的垃圾回收是自动进行的,无需手动管理每个对象的内存释放。遵循良好的编程实践和内存管理原则,可以帮助垃圾回收器更有效地工作,并确保应用程序的内存使用保持在合理的范围内。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值