as3.0的垃圾回收机制

还是同样的博客,还是同样的作者(Daniel Sidhion),但这次我要翻译的是他的另一篇文章——as3垃圾回收机制。      
      原文链接:http://hub.tutsplus.com/tutorials/understanding-garbage-collection-in-as3--active-4412
      下面进入正文:

      由于这篇文章的作者没有放原例子,所以下面的例子是我根据这篇教程一步步的整理出来,原理简单...看吧!!

       你是否在使用flash应用程序的时候注意过延迟?还不知道那些很酷的flash游戏为什么在你电脑上运行得很慢?你是否想知道更多可能潜在的原因,这篇文章是给你的。

       第一步:一个快的运行

       在我们进入真正的主题之前,你首先需要知道一点在AS3.0中实例与引用是如何工作的。如果你已经读过它,我仍然推荐阅读这微小的一步。所有的知识在你的头脑中都会是新鲜的。

       在as3.0中实例化和实例的引用是不同于大多数人想象中那样的。某些东西的实例化只是代码请求创建一个对象。通常,这种行为通过"new"关键字,但这也存在在当你使用 a literal syntax或者定义参数的函数,例如,这方面的例子如下:

 

// Instantiation through the "new" keyword
new Object();
new Array();
new int();
new String();
new Boolean();
new Date();

// Instantiation through literal syntax
{};
[];
5
"Hello world!"
true

// Instantiation through function parameters
private function tutExample(parameter1:int, parameter2:Boolean):void

   一个对象被创建之后,它仍然保持独立直到某物引用它。为了去做这事情,你一般会创建一个变量,把对象的值赋给变量,让它知道目前被哪个对象控制。不管怎样,当你把一个变量的值赋给另一个变量时,你没有创建一个新的对象。而是为对象创建了另一个连接。看下面的图片:

 


as3.0的垃圾回收机制
 

 

 

       图片呈现Variable1和Variable2都能控制笑脸(即它们能持有相同的类型)。左边,只有Variable1存在。无论如何,当我们创建并设置Variable2有Variable1相同的值时,我们没有在Variable1和Variable2之间创建一个关联(图片的右上部分),反而我们在笑脸跟Variable2之间创建了一个关联(图片的右下部分)。

       有了这些知识,我们可以跳到垃圾回收机制了。

       第二步:每个"城市"需要一个垃圾回收机制

       显然易见每个应用程序运行都需要一个确定数量的内存,它需要变量去控制值和使用它们。应用程序如何管理不再需要的对象。应用程序应该重复利用这些对象吗?应该删除它们吗?应该储存直到应用程序关闭吗?这三个观点可能发生,但在这里,我们具体谈谈第二个和第三个。

       假想一下,当应用程序初始化创建了很多对象,一旦这个周期结束超过一半被创建的对象仍未使用。它们存在于内存中会发生什么?它们毫无疑问会在内存中占用很多空间,这样就导致了人们所说的延迟。大多数用户不会喜欢这样,所以我们需要避免这样。我们如何编写代码才能使应用程序运行更有效率?答案就在垃圾回收机制中。

       垃圾回收机制是从内存管理形成的。目的是为了清除任何不用并占用系统内存空间的对象。这样的应用程序可以用最小的内存使用来运行。让我们看看它是如何工作的:


as3.0的垃圾回收机制
  

       当你的程序开始运行,它向系统请求一定量用于程序的内存。程序开始然后任何你需要的信息来填充这些内存。不管怎样,如果内存消耗接近一开始请求的内存,垃圾回收机制就会运行,寻找任何不需要用的对象来清空内存中的一些空间。由于对象搜索的大开销,有些时候这样会导致应用程序的一点延迟。

       图片中,你可以看见内存的峰值。垃圾回收机制会导致内存的峰值和急剧下降,当某些行为使应用程序达到请求内存,清除所有不需要对象。

       第三步:打开swf文件

       现在我们知道垃圾回收机制能为我们做什么,是时候去学习如何编写可以从中获得所有利益的代码了。首先,我们需要知道垃圾回收机制是如何运行的。当对象不能被访问,代码会知道它不能再被使用,所以它会被回收。

       Actionscript3.0通过垃圾回收的根检查可达性。此刻的对象不能通过垃圾回收的根访问,它会变成垃圾。下面你会看到一列它的原则。

       1.包级别的变量和静态变量
       2.局部变量和在一个正在执行的方法的变量或函数范围的变量
       3.从应用程序的主类或显示列表实例化的变量

       为了了解垃圾回收机制是如何处理对象,我们必须编写代码 并检查例子中发生了什么。我将会使用FlashDevelop的AS3项目,但我假设你能使用你想的任何IDE。我创建一个简单的带有一个按钮和文本框的文件。当按钮被点击,一个方法执行。在场景中显示我们想要的文本。

       我们的例子的目的是创建对象,删除它们并检查在它们删除后发生了什么。我们需要一个方法去判断对象是否存活。,所以我们给每一个对象添加一个ENTER_FRAME事件,同时在它们存活时让它们每秒显示一些文本。让我们编写第一个对象!

       我创建一个有趣的笑脸图片对象。每个对象在它的上端会有一个数字。所以我们将第一个对象定义为TheObject1,第二个为TheObject2,所以这样会很容易区分。

  1. private var _theObject1:TheObject1;
  2. private function newObjectSimple1(e:MouseEvent):void
  3. {
  4.     // If there is already an object created, do nothing
  5.     if (_theObject1)
  6.         return;
  7.      
  8.     // Create the new object, set it to the position it should be in and add to the display list so we can see it was created
  9.     _theObject1 = new TheObject1();
  10.     _theObject1.x = 320;
  11.     _theObject1.y = 280;
  12.     _theObject1.addEventListener(Event.ENTER_FRAME, changeTextField1);
  13.      
  14.     addChild(_theObject1);
  15. }
复制代码

      
      第二个对象代码的编写几乎一样。

  1. private var _theObject2:TheObject2;
  2. private function newObjectSimple2(e:MouseEvent):void
  3. {
  4.     // If there is already an object created, do nothing
  5.     if (_theObject2)
  6.         return;
  7.      
  8.     // Create the new object, set it to the position it should be in and add to the display list so we can see it was created
  9.     _theObject2 = new TheObject2();
  10.     _theObject2.x = 400;
  11.     _theObject2.y = 280;
  12.     _theObject2.addEventListener(Event.ENTER_FRAME, changeTextField2);
  13.      
  14.     addChild(_theObject2);
  15. }
复制代码


       代码中,newObjectSimple1()和newObjectSimple2()是鼠标点击按钮事件执行的方法。方法中创建一个对象并显示在舞台上,所以我们知道它被创建了。此外,每个对象侦听了ENTER_FRAME事件。只要它们还存活,就会隔秒显示一个信息。

  1. private function changeTextField1(e:Event):void
  2. {
  3.     // Our example is running at 30FPS, so let's add 1/30 on every frame in the count.
  4.     _objectCount1 += 0.034;
  5.      
  6.     // Checks to see if _objectCount1 has passed one more second
  7.     if(int(_objectCount1) > _secondCount1)
  8.     {
  9.         // Displays a text in the screen
  10.         displayText("Object 1 is alive... " + int(_objectCount1));
  11.          
  12.         _secondCount1 = int(_objectCount1);
  13.     }
  14. }
复制代码
  1. private function changeTextField2(e:Event):void
  2. {
  3.     // Our example is running at 30FPS, so let's add 1/30 on every frame in the count.
  4.     _objectCount2 += 0.034;
  5.      
  6.     // Checks to see if _objectCount2 has passed one more second
  7.     if(int(_objectCount2) > _secondCount2)
  8.     {
  9.         // Displays a text in the screen
  10.         displayText("Object 2 is alive... " + int(_objectCount2));
  11.          
  12.         _secondCount2 = int(_objectCount2);
  13.     }
  14. }
复制代码

<ignore_js_op style="word-wrap: break-word; color: rgb(51, 51, 51); font-family: Tahoma, Helvetica, SimSun, sans-serif;"> 版本1.rar (468.98 KB, 下载次数: 8) 

       第四部:删除对象

       现在我们已经完成了对象的创建。你是否曾经想过你删除一个对象后发生了什么(删除所有的引用)?它会被回收?现在我们测试它。我们创建两个删除按钮,分别对应两个对象。

  1. private function deleteObject1(e:MouseEvent):void
  2. {
  3.     // Check if _theObject1 really exists before removing it from the display list
  4.     if (_theObject1 && contains(_theObject1))
  5.         removeChild(_theObject1);
  6.      
  7.     // Removing all the references to the object (this is the only reference)
  8.     _theObject1 = null;
  9.      
  10.     // Displays a text in the screen
  11.     displayText("Deleted object 1 successfully!");
  12. }
复制代码
  1. private function deleteObject2(e:MouseEvent):void
  2. {
  3.     // Check if _theObject2 really exists before removing it from the display list
  4.     if (_theObject1 && contains(_theObject2))
  5.         removeChild(_theObject2);
  6.      
  7.     // Removing all the references to the object (this is the only reference)
  8.     _theObject2 = null;
  9.      
  10.     // Displays a text in the screen
  11.     displayText("Deleted object 2 successfully!");
  12. }
复制代码


<ignore_js_op style="word-wrap: break-word; color: rgb(51, 51, 51); font-family: Tahoma, Helvetica, SimSun, sans-serif;"> 版本2.rar (469.51 KB, 下载次数: 15) 
       如你所见,如果你点击"Create Object1"然后点击"Delete Object1",什么也没发生!我们可以说代码运行了,因为文本出现在舞台上,但为什么对象没有被删除?对象仍在这里因为这没有完全删除。当我们清理所有引用,我们告诉代码使它成为垃圾,但垃圾回收机制从未运行。记住垃圾回收机制只会在当前内存消耗接近一开始程序请求的内存。但我们该如何测试它?

       我肯定不会去写一大片代码用无用的对象去填满应用程序直到内存消耗过大。我们将使用一个当前Adobe不支持的方法。根据Grant Skinner(http://www.gskinner.com/blog/archives/2006/08/as3_resource_ma_2.html)的文章,强制运行垃圾回收机制。这个方法,我们触发一个简单的方法并看到在运行时发生什么。从现在开始,我会把垃圾回收机制叫为GC。

  1. private function forceGC(e:MouseEvent):void
  2. {
  3.     try
  4.     {
  5.         new LocalConnection().connect('foo');
  6.         new LocalConnection().connect('foo');
  7.     }
  8.     catch (e:*) { }
  9.      
  10.     // Displays a text in the screen
  11.     displayText("----- Garbage collection triggered -----");
  12. }
复制代码


       这个简单的方法,只是创建了两个LocalConnection()对象,意味着强制运行GC,我不推荐在严格的应用程序中使用这个方法。如果你是做测试,这样没有真正的问题。但如果是分配给其他人的应用程序,这不是一个好用的方法,以后会引起不可预料的效果。

 版本3.rar (469.89 KB, 下载次数: 8) 

       删除一个对象后触发GC,这真正的删除了这个对象!注意,如果你不删除对象但触发了GC,什么也不会发生,因为对象还有一个引用。现在,我们尝试一个对象保持两个引用并删除其中一个。

       第五步:创建另一个引用

       现在我们证实了GC工作后是我们想要的效果。让我们尝试其他:为一个对象(Object1)连接另一个引用并删除原型。

  1. private function saveObject1(e:MouseEvent):void
  2. {
  3.     // _onSave is a Boolean to check if we should link or unlink the reference
  4.     if (_onSave)
  5.     {
  6.         // If there is no object to save, do nothing
  7.         if (!_theObject1)
  8.         {
  9.             // Displays a text in the screen
  10.             displayText("There is no object 1 to save!");
  11.              
  12.             return;
  13.         }
  14.          
  15.         // A new variable to hold another reference to Object1
  16.         _theSavedObject = _theObject1;
  17.          
  18.         // Displays a text in the screen
  19.         displayText("Saved object 1 successfully!");
  20.          
  21.         // On the next time this function runs, unlink it, since we just linked
  22.         _onSave = false;
  23.     }
  24.     else
  25.     {
  26.         // Removing the reference to it
  27.         _theSavedObject = null;
  28.          
  29.         // Displays a text in the screen
  30.         displayText("Unsaved object 1 successfully!");
  31.          
  32.         // On the next time this function runs, link it, since we just unlinked
  33.         _onSave = true;
  34.     }
  35. }
复制代码


       如果我们现在测试,我们会发现如果我们创建Object1,然后保存它,删除它和强制执行GC,什么也不会发生。那是因为,即使我们删除原本的对象的关联,还有另一个引用。这使它不符合垃圾回收的条件。这基本上是所有你需要知道的关于GC。在这之后,我们该如何应用它到我们当前环境中?我们如何运行这知识去预防我们的程序运行缓慢?第六步将会介绍给我们:

 版本4.rar (470.41 KB, 下载次数: 10) 

       第六步:使你的代码有效率

       现在是最好的部分:使你的代码与GC高效地工作!这一步会提供很有用的信息。首先,我想介绍一个新的方法在你的应用程序里创建你的对象。这很简单,但需要有效地与GC合作。这个方法介绍两个简单的类。

       这个方法的思想是实现一个方法-名字叫destroy(),每一个你创建的对象,在这个对象完成工作的时候调用它。这个方法包含了所有必要的代码去删除所有对象的引用(不包括用于调用方法的引用),所以你要确保对象完全离开你的应用,容易识别GC。这样做的原因的解释在下一步。下面看一下这个方法的大致代码。

  1. // Create this in every object you use
  2. public function destroy():void
  3. {
  4.     // Remove event listeners
  5.     // Remove anything in the display list
  6.     // Clear the references to other objects, so it gets totally isolated
  7.      
  8. }
  9. // ...
  10. // When you want to remove the object, do this:
  11. theObject.destroy();
  12. // And then null the last reference to it
  13. theObject = null;
复制代码


       在这个方法,你会清除对象的所有东西,所以它完全与程序分离。在这之后,这将会更容易让GC定位和删除这个对象。现在看看一些最大内存错误发生的情况。

       1.只用于执行一个段时间的对象:你需要注意这个,它们可以消耗大量内存。这些对象只存在一段时间(例如,一个存储值的函数运行)和它们不经常被访问。记得删除它们的所有引用在你完成它们之后。否则,你的程序中会有很多这样的对象。如果你创建了很多它们的引用,你必须通过destroy()方法清除每一个。

       2.离开显示列表的对象:如果你想删除一个对象经常从显示列表中删除它。显示列表是垃圾回收机制的最根本。所以当你删除对象时保持你的对象远离它是很重要的。

       3.舞台,父亲和根引用:如果你喜欢大量的这些特征,记住当你完成时删除它们。如果你有大量有这样的引用的对象,你会有麻烦!

       4.时间侦听:有时让你的对象接受回收的是一个事件侦听引用。记住删除它们,或令它们成为弱引用,如果需要。

       5.数组(Array)和向量(Vectors):有时你的数组和向量会有其他对象,在它们离开引用时你可能不知道。注意数组和向量。


       第七步:

       虽然与GC一起工作是顺利的,但不完美。你必须注意你正在做得事情,否则你的程序会发生坏事情。我想证明一个问题,你不需要跟随所有必要的步骤而使你的代码更合适的与GC工作。

       有时候如果你不把一个对象的所有引用清除,你可能有这个问题,尤其是你把很多对象连接在一起。有时,一个简单的引用的离开足以让这发生:你的所有对象的引用的岛屿(island),所有对象与其他都有联系,不允许GC删除它们。

       当GC运行时,会执行两个简单任务去检查要删除的对象。计算每个对象有多少个引用。所有对象有0个引用的同时回收。另一个任务是,检查那堆互相连接的对象,但不能被访问,如此浪费内存。


as3.0的垃圾回收机制
  
       如你所见,绿色对象不能被延伸,但它们的引用计数为1。GC执行第二个任务检查这一块对象并删除它们全部。无论如何,当这一块太多,GC“放弃”检查和假设对象能延伸。


as3.0的垃圾回收机制
  
       这是引用的岛屿(island)。这会花费系统很大量的内存。同时因为它的复杂而不会被GC回收。这听起来很糟糕,对吧?这很容易避免,但需确保清除对象的一切引用。然后这可怕的事情就不会发生了。

       总结:

       现在到这里了。我们学习到为了减少延迟和内存使用让我们的代码更有效率。为了做到这样,我们必须了解在as3中对象的引用是如何工作的,如何使GC与我们的应用程序更有效地合作。当这样做的时候我们必须小心,否则它会变得更加棘手和缓慢!

       我希望你喜欢这个简单的技巧。

 

你可能感兴趣的:(垃圾回收机制)