flash垃圾回收和内存泄露

GC的原子模型

之所以用"原子模型"(Atomic Model)这个词,是因为这篇文章是用来描述GC怎么样在 player中工作的模型,但是不包涵其相关的技术实现或细节描述。
之所以这样是因为:
1.GC在实际中的运行行为甚为复杂并且很难区描述(就像我们其实并不知道原子是什么构成的,但是我们却可以用模型来解决问题)
2.player有时会干预GC
3.这个模型已经为我工作了很久了

Flash Player 内存管理

Flash持有的内存块被分配成很多等量的小内存块

flash会一次向操作系统申请几大块儿内存(所以flash不会经常向系统申请内存)
在flash中把从操作系统申请的一大块内存作为一个内存池,然后将内存池分割成包含很多很小且大小固定的块(block).
大的块用来存储 Bitmap, 文件等内存大户,而且他们是不可合并的
图片1.png
下载 (62.46 KB)
2010-9-7 15:37



当一个内存池用完时,flash会向操作系统再申请一大块内存.

图片2.png
下载 (25.85 KB)
2010-9-7 15:37


GC并不是总在运行

你不知道什么时候会垃圾回收

当某些对象被释放后,他们在内存中所占用的块(block)并不一定在下一次分配内存时被重用.

假设我们当前内存使用大小为100000bytes
图片3.png
下载 (31.92 KB)
2010-9-7 15:37



假设Foo实例大小为 512bytes
当分配内存时,它会占用flash内存池中新的一个512bytes单元
foo = new Foo()
图片4.png
下载 (32.95 KB)
2010-9-7 15:37


当我们清除一个对象所有引用后,此时对象占用的内存并没有立即被标记为未使用,只有GC将其标记为未使用后,该对象所占用的块属性才会变为未使用.

内存仍然为100512 bytes.
将其置空(销毁)并不代表其在内存中已经被释放.
System.totalMemory = 100512 bytes
图片5.png
下载 (38.93 KB)
2010-9-7 15:37


我们再次实例化一个新的Foo对象,它会占用flash内存池中新的单元(未使用的单元)
内存现在是101024 bytes.
图片6.png
下载 (42.76 KB)
2010-9-7 15:37


只有内存分配才会引发垃圾回收.


当一个内存池快被用完时,在向操作系统申请内存之前前,GC就会尝试去收集内存池.

就像影片租赁一样,他们不会去检查最近还回来的影片,而是要等货架上所有的影片都没了的时候才会去检查.
图片7.png
下载 (39.92 KB)
2010-9-7 15:37



只能被内存分配才会引发GC;这就意味着,你总可以在内存中看到一些未被回收的对象,并且它们一直占用内存.


GC回收一次内存并不一定会将所有的可回的对象全部回收
  因此不要企图通过渲染或者 交互来干预GC
这就意味内存永远不可能回复到最初的状态.

图片8.png
下载 (49.09 KB)
2010-9-7 15:37

图片9.png
下载 (49.66 KB)
2010-9-7 15:37


收集器会尽量将所有标记为"未使用"的块儿整理到同一个flash内存池中,当某一个flash内存池中所有小的单元都是"未使用"时,flash就会将其释放,返还给操作系统.
        一般情况下GC不会一次就完成这一过程(译者注:在ActionScript 3.0 and
        AVM2 erformance Tuning这篇文章中提及每次标记时间限制在30毫秒内).
因此内存永远都不可能恢复到出事状态.
图片10.png
下载 (29.63 KB)
2010-9-7 15:37

图片11.png
下载 (42.57 KB)
2010-9-7 15:37


由于GC是不可确定的,这就意味着你不能严格的确认你的 程序是否出现了内存泄露
但是我们可以靠经验来判断。 检测内存泄露

一些不起眼的事件例如timing事件,鼠标事件和键盘事件都会影响总内存。
   因此交互不是一个很好的检测方法.


一般情况我们可以关注一些可重复的过程
  popup对话窗口生成和删除
  Modules的加载和卸载

如果在一段时间内重复执行这一过程,那么内存总量会在达到某个最高值后停止或者下降,当然前提是你的程序没有内存泄露

在flex下一次释放内存时, 就会知道否有有内存泄露的症状.
在此之前:
        编写as代码使其过程自动化,即使整体流程的执行不依赖于交互.

         其中一种技术就是使用switch语句并伪造鼠标和键盘事件
  1. private function doit():void { // call this on some interval
  2.     var m:MouseEvent;
  3.     switch (currentStep)
  4.     {
  5.         case 0:
  6. m = new MouseEvent(MouseEvent.CLICK);
  7. b.dispatchEvent(m); // click app button to open dialog
  8. break;
  9.         case 1:
  10.         case 2:
  11. // wait for dialog to appear
  12. break;
  13.         case 3:
  14. m = new MouseEvent(MouseEvent.CLICK);
  15. var o:Object = loginDialog;
  16. o.b.dispatchEvent(m);  // click dialog button to close dialog
  17.         case 4:
  18.         case 5:
  19. break;
  20.         default:
  21. currentStep = -1;  // start over again
  22. break;
  23.     }
  24.     currentStep++;
  25. }
复制代码
去哪里找内存泄流的源头

( 原文:They will get reassigned every time through the sequence so they will drop their reference to the previous object )
1.对于一个可重复的过程, 属性引用到的对象是不会造成内存泄露的
   因为每一次的执行,他们都会被重新赋值.


( 原文:Arrays, Objects used as Maps, Dictionary are common causes )      
2.把Array,Objects当做Maps,Dictionnary来用,可能会引起内存泄露
      
         (原文 : Run the sequence once, see how many things are in the arrays and object maps)
        运行一次该过程,看array 或 object maps存有多少个对象
       
         ( 原文: Run it several more times, are there more things? )
        运行多次再观察它们所包含的对象数量
3.没有移除监听器

GC怎样工作的

(原文:Start at known tops of Object trees. )
从已知的对象树(Object trees)的顶开始

(原文:Mark them and any objects they reference)
1.标记当前"节点"所有引用到的对象

(原文: Go through the heap and free anything that isn’t marked.)
2.检查堆,"释放"没有被标记的对象.

(Three tops of Object trees
3.对象树的3种顶层类型
    (Stage)
     Sttage

    (ApplicationDomain/Class definitions)
     ApplicationDomain/类定义(Class definitions)

     (Stack/Local Variable)
      栈/本地变量

Stage
     从 Stage开始你可以跟随箭头(引用)找到所有的蓝色盒子(被使用对象)
      图片12.png
下载 (53.03 KB)
2010-9-7 16:07


ApplicationDomain/Class definitions
   只有静态变量会产生影响
    图片13.png
下载 (63.29 KB)
2010-9-7 16:07


栈(stack)/本地变量(Local Variables)
   图片14.png
下载 (68.16 KB)
2010-9-7 16:08



移除监听器

对子容器添加监听器不会引起内存泄露
下面的监听器不强制要求移除(但是移除所有监听器是一个好习惯)
图片15.png
下载 (73.12 KB)
2010-9-7 16:08


子容器的监听列表会保存一个父级容器事件处理函数的引用
当子容器被从父级容器中移除后,就不可能通过Stage访问到了
图片16.png
下载 (47.06 KB)
2010-9-7 16:08


监听父级对象时会造成内存泄露
  必须要移除对父级容器的监听器
  
图片17.png
下载 (57.23 KB)
2010-9-7 16:08

        有两种2个路径可以到达PopUp
        图片18.png
下载 (59.33 KB)
2010-9-7 16:08

      
         在这里使用弱引用可能会会更好些
                       除非你可以确认你和你的父级已经从显示列表中删除了.       
         下面的监听器不强制要求删除。
          图片19.png
下载 (72.65 KB)
2010-9-7 16:08


总结
1..GC是内存分配引起的而非程序中的删除
2.GC时不可预测的
3.使可重复过程自动化
     随着时间的推移,总内存应该会有一个最大值.

4.出乎你意料的是:事件监听会造成反向引用.
     Dispatcher引用监听器

例子代码
原文ppt
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值