Flash务实主义五——AS3的垃圾回收

GC和内存泄露无关

垃圾回收,这次是一个被无数人讨论过的传统话题。

Action Script使用的是和Java相似的内存管理机制,并不会即时回收废弃对象的内存,而是在特定时间统一执行一次GC(Gabage Collection)操作来释放废弃对象的内存,避免了重复判断是否需要回收产生的性能问题。

但要注意,这只是决定回收的时机,而不是回收的内容。这个延迟执行内存回收也就是个表面的现象,不管什么时候执行GC,能够回收的内存最终都能回 收,不能回收的肯定不能回收。唯一的影响是,因为回收是延迟执行的,你在查看内存的时候不能直观地看到因为一个对象被废弃而回收内存的过程,会产生迷惑。

但这对于解决内存泄露是无关紧要的。

内存泄露指的就是当你销毁了一个对象的时候,它占用的内存却无法被回收,这会导致可用内存越来越小最终溢出,在内存紧张的环境中将会造成系统崩溃。其原因多种多样,但一般都是开发者的疏忽所致,没有提供给系统足够的可以销毁对象的依据。

执行GC虽然和内存泄露没有关系,但是如果不在测试前执行GC,你将看不到当时实际的不可回收内存的量,而内存泄露就是指不可回收内存的数量的增 加。因此,测试内存回收将离不开GC方法。没有使用GC方法的测试用例是没有意义的,因为这其中掺杂了偶然性(什么时候执行GC)。不少荒谬的测试结果都 是因为没有在正确的位置执行GC导致的。

Flash Player虽然没有开放发布状态的手动gc,但调试版本是可以使用的,正好可以让我们测试。此外下面的HACK代码也可以在发布阶段触发GC。

try {
    new LocalConnection ().connect ( "gc" );
    new LocalConnection ().connect ( "gc" );
} catch ( e:Error ) {}

但我再次强调,调用GC仅仅是用于测试。实际产品中调用GC基本没有意义(除了用于控制GC时机),总之如果你的程序出现了内存泄露,那一定和GC没有关系,请不要再在这种地方浪费宝贵的时间与精力。

只有在申请内存时才会触发自动GC

AVM2的GC是在每次申请内存时,根据当前内存占用来触发的。申请内存是一个必要因素。所以,如果你一直不进行申请内存的操作,就算内存达到了一个高值,它也不会进行GC。

这确实是个不合理的地方。但是,在实际环境中,一直不请求内存的情况是很少见的,就算出现,当时也未必处于内存的高值。这种情况主要出现在测试环境中,导致一些人会怀疑自动GC的功能是否正常。实际上这也是没有必要的。

Flash中垃圾回收的条件

在AVM2中,除去特殊的BitmapData必须调用dispose才能回收内存外,其他的部分都是用引用计数法和标记清除法作为判断是否应该回收内存的手段,而且并没有提供主动回收的API,详细部分请看这篇日志,我就不重复了。

http://www.cnblogs.com/cos2004/archive/2010/11/07/1870980.html

因此,你要回收一个对象,只要保证没有任何对象引用它,而且他的方法没有被当做事件函数——或者说,他和程序的其他部分已经没有任何联系,它就满足 了引用计数法的标准,就一定会被回收。做到这一点的方法就是一般说的“执行removeChild,removeEventListener,将对他的引 用设置为null”。

但是,实际上回收一个对象的要求并没有那样严格,就在于FP除了引用计数法,还包括标记清除法。标记清除法是从程序的根对象开始(stage,静态 属性,活动的定时器和加载器,ExternalInface.callBack)一级一级遍历对象,只要遍历不到,即使不满足引用计数法的条件也可以回 收。比如两个对象互相引用,但是和外界都没有关系,形成了孤岛,它们就可以被回收,尽管它们因为互相引用使得引用数不为0。比起单纯的引用计数,这种办法 能确实能找到已经无法再访问到的实际上的闲置对象。所以,可以看到很多人的代码实际上并没有设置null,甚至没有 removeEventListener,它一样可以被正常回收,少写这些代码可以使得程序更简洁,要全部符合标记清除法的条件,会很累。

“无法被根访问”,这种说法很暧昧,基本不能当做判断依据。所以我下面会举几个具体例子,来说明什么样的情况是符合标记清除法的要求的。

首先明确一点,标记清除法是只以能否能被根访问作为唯一依据的,并不需要关注被引用的次数,请不要混淆。

  • 属性的相互引用是很明确的,一般都是一个对象包含着若干属性,那么这个对象自然可以维持它的属性的引用。如果这个类不会被回收(能够被根 访问),他的所有属性也都不会被回收。同样的,如果这个类可以被回收的话(不能被根访问),也就不会妨碍属性的回收。所以你并不需要将所有属性设置为 null,除非你希望在对象存在时候就回收其属性的内存,这种需求基本不存在。
  • 静态属性是一个特殊的情况。静态属性本身就是根,所以你必须将其设置null才有可能被回收,没有别的办法。
  • 至于在显示列表中的对象。既然根(stage)可以用getChildAt访问到自己的所有子对象,那么只要你在显示列表中,就肯定不会 被回收。然而,如果显示对象的父层对象已经不再显示列表内,它的子对象就算还在父层对象之中也没有关系,因为它已经不能被stage访问到了。所以你不需 要removeChild各层的全部对象,而只需要removeChild最高一层的父对象即可。
  • A.addEventListener(“event”,B.handler),像这样添加过事件后,你可以认为B.handler成为 了A的一个属性(因为A在需要的时候要能调用B.handler),这里也符合属性相互引用的原则。但是事件判断起来的确要比属性麻烦,因为相互引用的情 况很多。在这里可以分为三种情况:
    1. 对自己监听自己的事件,这相当于用自己的属性保存自己引用,任何情况都不会阻碍自己被回收。
    2. 对自己的子对象(属性或者child)监听自己的事件。因为子对象本来就是自己在维持它的引用,那么即使它们会维持你的引用,也 只会形成一个循环。一旦你和stage脱离了联系,子对象同样也会脱离联系,当然也无法妨碍你自己被回收了。除非子对象因为一些原因可以单独维持引用(诸 如被保存在静态属性中),但这种情况很少见。
    3. 对自己的父对象(parent或者stage)监听自己的事件。因为这使得你成为了父对象的一个属性,只要parent或者 stage不被回收,那么自己就不会被回收。尤其是stage,它肯定不会被回收。这种情况一般都会导致自己无法回收,是必须 removeEventListener的。

总得来说,就是务必注意对stage,parent的事件监听,其他情况一般都是不会妨碍回收的。而对stage,parent的监听大多都是各种鼠标,键盘事件。数量并不多,专门注意这里可以杜绝大部分因为事件造成的内存泄露。

其实,内存泄露并不容易出现。按照普通的编程习惯,只有监听stage事件这种做法会造成意料之外的泄露,一般都是可以顺利回收的。这比每次都要手工回收内存要方便多了。

这里只有BitmapData是例外。除了遵从上面的规则外,要回收它的内存,必须手动调用dispose方法,习惯自动回收的人会很累。务必注 意,Bitmap对象的bitmapData属性是需要手动销毁的,Loader加载的位图是需要手动销毁的,当你用一个生成的位图作为位图填充绘制平铺 的图像后,在销毁这个图像后也必须销毁这个位图(所以你必须一直保存位图的引用)。BitmapData是32位的未经任何压缩的图像,随便一个体积都会 非常大,不处理好它们的回收,一个BitmapData泄露就可以顶你数万个复杂对象的泄露。

如果出现非常明显的内存泄露,大部分时候都是位图泄露。所以在研究上面的引用计数法和标记清除法以及GC之前,请先保证位图部分不出问题。

弱引用时的例外

弱引用会改变垃圾回收的规则。如果使用了弱引用,addEventListener将不会影响对象回收,即使对stage添加监听,也不会导致自己 被回收。但是这同时也是缺点,因为有的时候你就是希望用引用限制住对象的回收,使用弱引用会使得这个对象有时回收有时不回收。虽然极少出现,但一旦出现, 这种不容易重现的错误是很难查出来的。因此我并不推荐使用弱引用。

弱引用在AVM2中只有两处:

  • 一处是addEventListener的第5个属性,名为userWeakReference,设置为true,监听事件将不会影响对象回收。
  • 一处是Dictionary的构造函数参数,名为weakKeys,设置为true,当键为复杂对象时,即使Dictionary存在,键依然可以被回收。注意,这里说的是键,不是值,值是不享受弱引用待遇的。这个属性也写得也很明白,是weakKeys。

内存泄露的查找方法

Flash Builder提供了一个概要分析工具,可以帮助我们查找内存泄露。大多数情况都可以帮助我们解决问题。可以查看下面的文章:

http://blog.csdn.net/bbmjfpig/archive/2010/12/30/6107347.aspx

关键点在于,检测内存泄漏应该是“创建,取样,销毁,再创建,取样”,然后以两次取样的对比数据来观察泄露。因为对象在第一次创建时会有一些缓存数 据,它们在设计上就不会随着对象销毁而回收的,比如类定义的缓存,比如皮肤。它们只会创建一次,和我们看到的泄露并不是一回事。

必要时可以执行强制GC

因为每次GC都需要消耗性能,对象越多,GC越慢。我理解Flash Player禁用发布版本的System.gc()是为了避免开发者滥用这个方法,但有些时候我们的确需要手动控制GC时机,因为GC过程如果遇到大量可回收对象会让Flash Player卡住。

比如,我们需要在切换屏幕时回收一次内存,这时候卡是看不出来的,而不是切换完后播放动画时回收然后让动画顿住。或者,我们会定期在必要的时候执行 一次GC,将GC需要的时间分担开。所以这时候用HACK方法强制执行一次GC也不失为一个选择。当然,这和内存泄露半点关系都没有。

Flash Player这个地方的设计特别的不好。它自己又不支持分步GC,一旦GC的时候没有办法避免卡的问题。结果GC的时机还不给控制……

微量剩余内存

测试中FLASH的确存在微量内存无限增加的问题,原因未知。我将50万个对象扔在一个数组中,销毁后确实会多出1M的内存占用(如果没扔在数组中不会),但这个数量很小,但达到能看得出来的100M内存需要5000万个对象,这个数额在通常情况下很难达到。

不过也有人说这只是对象销毁而内存并未全部释放的表现,实际上最后还是能完全释放的。或者是因为totalMemory的不精确所造成的。这个我就不清楚了。

不过就算这个的确是FlashPlayer的BUG,也无伤大雅吧。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


  译序:JWMediaPlayer是开源的网页使用的Flash播放器。本文采摘于JWPlayer的官方文档,讲解了JWPlayer对于RTMP的使用方法,我们可以从JWPlayer客户端的角度来了解RTMP协议。以下是官方原文:      简介    RTMP(RealTimeMessagingProtocol
    Flash编程原理都是只能将1写为0,而不能将0写成1.所以在Flash编程之前,必须将对应的块擦除,而擦除的过程就是将所有位都写为1的过程,块内的所有字节变为0xFF.因此可以说,编程是将相应位写0的过程,而擦除是将相应位写1的过程,两者的执行过程完全相反.一、Nor和NandFlash
 上传setenvgatewayip192.168.1.1;setenvserverip192.168.1.7;setenvipaddr192.168.1.156;mw.b0x820000000xff0x1000000sfprobe0sfread0x8200000000x1000000tftp0x82000000test.bin0x1000000 下载mw.b82000000ff1000000tftp82000000test.bi
Error:FlashDownloadFailed-"Cortex-M3"出现一般有两种情况:1.SWD模式下,Debug菜单中,Reset菜单选项(Autodetect/HWreset/sysresetReq/Vectreset)默认是AutoDetect,改成SysResetReq即可。2.Jtag模式下,主要是芯片大小选错。Flash->ConfigureFalshTools配置窗口,切换到“Utilities"
jPlayer是一个用于控制和播放mp3文件的jQuery插件。它在后台使用Flash来播放mp3文件,前台播放器外观完全可以使用XHML/CSS自定义。支持:有一点比较好的是,在支持html5的浏览器上会使用html5的标签audio或者video,而不支持的浏览器上使用swf来播放选择需要播放的Mp3文件。播放、暂停
#ifndef__FONTUPD_H__#define__FONTUPD_H__#include"sys.h" //字库信息结构体定义33字节__packedtypedefstruct{u8fontok;//字库存在标志,0XAA,字库正常;其他,字库不存在u32ugbkaddr;//unigbk的地址u32ugbksize;//unigbk的大小u32f12addr;//gbk12地址u32g
ROM(ReadOnlyMemory)和RAM(RandomAccessMemory)指的都是半导体存储器。ROM在系统停止供电的时候仍然可以保持数据,而RAM通常都是在掉电之后就丢失数据,但是访问速度快。典型的RAM就是计算机的内存。RAM有两大类,一种称为静态RAM(StaticRAM/SRAM),SRAM速度非常快,是目前读写最快的存储
JSpc端和移动端实现复制到剪贴板功能实现在网页上复制文本到剪切板,一般是使用JS+Flash结合的方法,网上有很多相关文章介绍。随着HTML5技术的发展,Flash已经在很多场合不适用了,甚至被屏蔽。本文介绍的一款JS插件,实现了纯JS方法复制文本到剪切板。插件名是Clipboard.js,该插件不依
例子:R0=1R1=1R2=10R3=e000ed10R12=0LR=fffffff9(中断返回值)PC=0PSR=60000013或60000016或60000036(Z、C、EXCEPT_NUM:RTC_WKUP_IRQn、EXTI0_IRQn、USART2_IRQn)BFAR=e000ed38(不关心)CFSR=20000(INVSTATE:Invalidstateusagefault thePCvaluestackedf
 内存接口概念首先来分析下操作GPIO控制器和操作UART控制器两者的区别如图是S3C2440是个片上系统,有GPIO控制器(接有GPIO管脚),有串口控制器(接有TXDRXD引脚)配置GPIO控制器相应的寄存器,即可让引脚输出高低电平;配置UART控制器相应的寄存器,即可让引脚输出波形。前者相对简单,类
小编导语:    近几年来,网页游戏成为了游戏界关注的焦点,由于其制作简单,成本低并且收益率较高,因此成为了众多游戏厂商追逐的对象,但是除了商家夸张的炒作宣传外,很少有页游佳作出现。然而,随着Unity3D游戏引擎的出现,网页游戏的3D化成了页游冲出重围的杀手锏,那么在flash网页游戏称
1.指定数组到特定的Flash单元#pragmalocation=0x000FFF00 __rootconstcharFlash_config[]={0x0,0x1,0x2,0x3,0x4,0x5,0x6,0x7,0x8,0x9,0xA,0xB,0xC,0xD,0xE,0xF,0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19,0x1A,0x1B,0x1C,0x1D,0x1E,0x1F,0x20,0x21,0x22,0x23,0
继续研究发现,计算机的固件真的很有趣。参考了一些重要的资料,比如http://donovan6000.blogspot.com/2013/06/insyde-bios-modding-advanced-and-power-tabs.html等,对于IDA的使用也了解了一些。最后,总结一下目前看来可行性的方案:0.基础知识储备,包括UEFIBIOS的概念,InsydeBIOS的
<!DOCTYPEhtml><htmllang="en"><head><metacharset="UTF-8"><title>navigator对象<itle></head><body><buttononclick="checkFlash()">检测</button>
修改网上流传的flash-marker.js(function(global,factory){typeofexports==='object'&&typeofmodule!=='undefined'?module.exports=factory():typeofdefine==='function'&&define.amd?define(factory
shareObject本地缓存存储位置:win7系统用户到C:\Users\[你的用户名]\AppData\Roaming\Macromedia\FlashPlayer\#SharedObjects\XP或2003用户到:C:\DocumentsandSettings\用户名\ApplicationData\Macromedia\FlashPlayer\#SharedObjects\ ---------------------作者:iteye_
安装谷歌浏览器之后经常遇到Flash崩溃或者浏览器在浏览Flash内容时卡死的情况。在网上查找资料大多都认为应该是浏览器自带的Flash插件工作模式引起的问题,解决方法如下:首先在地址栏输入chrome://plugins/显示浏览器使用的插件。点击右上角的详细信息,可以看到Flash插件为进程外
之前一直使用的W25Q16spiflash都没问题,换了一款W25Q80后发现工作不正常,经过测试,初步定位到问题在于初始化SPI后是否将CS拉高。于是又去查看了一下原厂代码:发现原厂的代码初始化SPI接口时是专门拉高CS的。结论:网上很多代码初始化SPI接口时没有专门拉高CS,对某些型号可能确实
======================================================NANDFlash最小存储单元:写数据操作:通过对控制闸(ControlGate)施加高电压,然后允许源极(SOURCE)和汲极(RRAIN)间的N信道(N-Channel)流入电子,等到电流够强,电子获得足够能量时,便会越过浮置闸(FloatingGate)底下的二氧化硅层(S
安装CnarioPlayer3.8.1.156或其他版本时,有时会出现如下提示:Warning4154.AdobeFlashPlayer13...notcorrectlyinstalled:请前往AdobeFlash网站,并选择下图示的版本下载安装: