当前位置: 首页 > news >正文

flex 垃圾回收

原文在这里:Garbage Collection with Flex and Adobe Air

我终于有时间来整理在flexcamp上演讲的东西并写篇博客了。就在flexcamp前一个月,我几乎天天和FlexProfiler争吵而且关系很紧 张(笔者注:结论不一致)。因此我觉得在flexcamp中讲讲性能监测和垃圾回收(GC)是非常合适的话题。没错,想要脱离GC去获得性能提升简直是不 可能的。如果你想改善你的应用的内存管理你必须知道Flashplayer(和adobe AIR)如何管理内存分配和释放。

有许多博客、文章、演讲、等等都介绍过GC和profiling,我在文章底部将他们列举了出来。对于每天使用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,被提供的对象的属性作为 值来源:

 xmlns:mx="http://www.adobe.com/2006/mxml" 
    borderStyle="solid" width="200" 
    >
>

	    [Bindable] private var _field1:String;
	    [Bindable] private var _field2:String;
	    override public function set data(value:Object):void {
		_field1 = value.field1;
		_field2 = value.field2;
	    }
	]]>
>
 text="{_field1}" />
 text="{_field2}" />
>


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

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:

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


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

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

建议性GC

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

转载于:https://www.cnblogs.com/tiandi/p/3451989.html

相关文章:

  • hibernate中session.flush()
  • [模板]区间第k大整体二分
  • 使用Docker快速部署Storm环境
  • 谈谈Java多线程
  • jzoj2866. 【集训队互测 2012】Bomb
  • python自动化运维技术读书笔记
  • js同步和异步
  • 并发并行同步异步多线程
  • 猿辅导 2019年 校招提前批笔试
  • RequireJs入门
  • Asp.net页面的生命周期
  • 终于弄好了 homework-09
  • python面向对象
  • leetcode 337. House Robber III
  • Durandal入门
  • [译] React v16.8: 含有Hooks的版本
  • 【399天】跃迁之路——程序员高效学习方法论探索系列(实验阶段156-2018.03.11)...
  • 【编码】-360实习笔试编程题(二)-2016.03.29
  • 78. Subsets
  • Android开发 - 掌握ConstraintLayout(四)创建基本约束
  • canvas 五子棋游戏
  • CNN 在图像分割中的简史:从 R-CNN 到 Mask R-CNN
  • css布局,左右固定中间自适应实现
  • Java Agent 学习笔记
  • JavaScript 事件——“事件类型”中“HTML5事件”的注意要点
  • JS字符串转数字方法总结
  • MySQL几个简单SQL的优化
  • Quartz初级教程
  • Sass Day-01
  • WordPress 获取当前文章下的所有附件/获取指定ID文章的附件(图片、文件、视频)...
  • 从零到一:用Phaser.js写意地开发小游戏(Chapter 3 - 加载游戏资源)
  • 开发基于以太坊智能合约的DApp
  • 融云开发漫谈:你是否了解Go语言并发编程的第一要义?
  • 如何进阶一名有竞争力的程序员?
  • 《TCP IP 详解卷1:协议》阅读笔记 - 第六章
  • 如何用纯 CSS 创作一个菱形 loader 动画
  • ​DB-Engines 11月数据库排名:PostgreSQL坐稳同期涨幅榜冠军宝座
  • ​ssh-keyscan命令--Linux命令应用大词典729个命令解读
  • ​决定德拉瓦州地区版图的关键历史事件
  • #pragma预处理命令
  • #我与虚拟机的故事#连载20:周志明虚拟机第 3 版:到底值不值得买?
  • (html5)在移动端input输入搜索项后 输入法下面为什么不想百度那样出现前往? 而我的出现的是换行...
  • (JSP)EL——优化登录界面,获取对象,获取数据
  • (论文阅读23/100)Hierarchical Convolutional Features for Visual Tracking
  • ****** 二十三 ******、软设笔记【数据库】-数据操作-常用关系操作、关系运算
  • .NET CORE 第一节 创建基本的 asp.net core
  • .NET/C# 如何获取当前进程的 CPU 和内存占用?如何获取全局 CPU 和内存占用?
  • .NET/C# 阻止屏幕关闭,阻止系统进入睡眠状态
  • .NET开发不可不知、不可不用的辅助类(一)
  • .net中应用SQL缓存(实例使用)
  • /etc/motd and /etc/issue
  • @selector(..)警告提示
  • [@Controller]4 详解@ModelAttribute
  • [8-23]知识梳理:文件系统、Bash基础特性、目录管理、文件管理、文本查看编辑处理...
  • [Android Studio] 开发Java 程序