【问题标题】:What exactly is garbage collection and how do you do it in ActionScript 3.0?垃圾收集到底是什么?如何在 ActionScript 3.0 中完成它?
【发布时间】:2010-11-04 16:03:34
【问题描述】:

我已经使用 ActionScript 编程大约 6 年了,但在 AS3 出现之前从未听说过“垃圾收集”这个术语。为什么我们现在必须担心它,而以前从来没有?确切地说,它是什么?从我读过/听到的内容来看,它与内存管理/泄漏等有关 - 甚至 我也不太了解,除了它与性能。

我最近为我的一位艺术家朋友创建了一个网站,它是在 AS3 中完成的。我注意到它占用了大量资源。显然这是我想要改进的地方。我猜这与没有垃圾收集的事实有关?!不幸的是,我一点也不知道从哪里开始,因为我觉得我需要更好地理解它是什么以及如何在 AS3 中专门做这件事。

对于好奇的,这里是 URL:http://www.jeffperrott.com

【问题讨论】:

    标签: actionscript-3 actionscript garbage-collection


    【解决方案1】:

    我对 AP3 的实现一无所知,但必须有人发布这个:)

    http://en.wikipedia.org/wiki/Garbage_collection_(computer_science)

    基本上,您使用指向数据的指针(嗯,引用)。超出范围的引用将被删除。只要没有引用指向数据,数据就会被标记为垃圾回收。您的 GB 线程(进程或守护进程)将及时释放用于分配标记数据的所有资源。

    【讨论】:

      【解决方案2】:

      通常,您或您的开发运行时必须在某些时候手动处理内存,即在内存中为您的应用程序数据创建空间或在您的应用程序完成后清除内存的使用。如果运行时没有 GC(垃圾收集器),您需要手动处理应用程序中变量和对象的内存空间的创建和删除。

      一个例子是原生 C++。在 C++ 程序员可以真正对应用程序做很多事情之前,他们必须做好自己处理内存的基础工作。他们向操作系统请求内存,他们添加分配,他们将对象和数据添加到内存,他们稍后删除对象和数据,他们取消分配内存,他们清理它并再次将其释放给操作系统。

      使用 GC,GC 会为您完成所有工作。您所做的只是告诉 GC(通常通过实例化一个对象)您计划使用内存,然后 GC 计算出您需要多少内存,向操作系统询问它,分配并添加您的数据。然后它会不时检查您的应用程序是否仍在使用该对象。如果不是,那么它会删除对象、取消分配、清理它并释放回操作系统。所有这些都是为您完成的,因此您,程序员,可以专注于您的程序。 VB 6.0 和 C#/VB.NET 就是一个例子。

      编辑:这是一个非常简化的解释。实际上还有更多内容,但这个答案应该回答这里提出的基本问题。

      【讨论】:

        【解决方案3】:

        垃圾收集器是清理未使用内存的运行时部分(在您的情况下是 Flash 或 AIR 播放器)。 ActionScript 一直像所有脚本语言(例如 javascript、perl、ruby 等)一样具有垃圾收集功能,您只是没有看到它对 AS 进行了太多讨论。

        基本思想是创建的每个对象都占用一些内存,并且您的变量保持对这些对象的引用。一个对象的内存不会“释放”回可用内存池中,直到不再有对它的引用。垃圾收集器跟踪哪些对象引用了它们,并在不同的时间间隔回收那些引用为零的对象的内存(即没有变量指向它,所以甚至没有代码可以找到它来使用它)。如果有任何引用*,则无法回收对象的内存,以防您的代码的某些部分决定将来使用它。

        如果您的程序似乎正在泄漏内存(不断增长,大小从未稳定或缩小),那么您可能将对象放置在散列或数组或其他一些集合对象中。这个对象显然维护了对其内容的引用,所以这些东西永远不会被删除。

        请注意,在 ActionScript 中,使用闭包作为事件处理程序很容易创建“泄漏”。如果您的处理程序可以访问已分配但从不为空的变量,则这些变量会保留它们的引用,并且只要注册了事件处理程序,它们指向的对象就不会被垃圾回收。

        function registerHandler(neverReleased:Object) {
            ...
            addEventHandler(function (e:Event) {
                    ...
                });
        }
        

        直接调用垃圾收集器几乎总是一件坏事(tm),Flash/Flex 中的垃圾收集器非常好。首先找出你的对象被存放在哪里,然后释放它们,让 GC 按照自己的时间表工作。

        *circular references 除外,这有点超出了这个答案的范围。

        【讨论】:

          【解决方案4】:

          补充查德威克的答案:

          如果给图片添加EventHandler,但在图片不再显示时不removeerEventHandler,图片占用的内存无法被垃圾回收器回收。

          【讨论】:

          • 在 AS3 中,您可以选择创建对事件侦听器的弱引用,这将允许图像(例如)被垃圾收集。
          • 是addEventListener,反之亦然。只要图像存在,监听事件的函数就不会被收集。
          • 你是绝对正确的。感谢您指出我的错误。
          【解决方案5】:

          每次您使用“new”关键字创建对象时,您都在将自己的主张押注在一块内存上 [1]。因此,假设您通过编写 var exampleUint:uint = new uint(); 来创建一个 uint。 ActionScript 对计算机说“嘿......我需要......四个字节的内存。”,计算机回复“好的,酷,使用从 [某个内存地址,如 0x7B3208C1] 开始的四个字节”[2] .所以当你说var someString:String = "blah";你在内存中声明一个空间,用一些信息填充它(在这种情况下,它是"blah"),创建一个标签(在这种情况下,它是someString)和bind标签到内存中的那个空间。

          现在标签实际上并不与数据本身相关联,它与内存中包含数据的位置相关联。这似乎令人困惑,因为它确实如此,但由于某些原因,它有很大的意义。 ActionScript 中的所有对象都是reference 类型,这意味着该对象包含一些数据的内存地址,而不是数据本身。这与 value 类型的对象形成对比,其中标签与数据本身相关联。不要担心值类型的细节,因为它不在 ActionScript 中,所以出于您的目的,我将它严格包含在内以进行对比。好吧,糊涂了?您可能应该这样做,因为这整个段落是不必要的,甚至可能是不必要的,但请尽量将其放在脑后,因为它可能与您的问题有关。

          现在,当该标签消失时(例如,它从 scope 中消失,所以现在 someString 没有意义),只有标签 someString 被删除。所以对于这个例子,如果你说someString = null;,字符串"blah"实际上还在内存中,但是标签someString没有引用那条信息,它引用了the null object的内存地址(再次,细节)。我们需要一种方法来向计算机指定“你知道我声称从 [whateverAddress] 开始的 [whateverSize] 内存块吗?好吧,我现在已经完成了,所以其他人可以使用那块内存”。 Gabage collection 是一个自动回收您已声明的内存的过程,而不必自己担心。在没有垃圾收集的语言中,new 有一个补充关键字,专门释放与对象关联的内存 [3],但 ActionScript 没有。你只需记住一段记忆,然后忘记它。

          在某个地方,您可能已经创建了另一个变量并引用了那条数据,var someOtherString:String = someString;,所以我们不能只说“好吧,标签someString 不见了,所以让我们摆脱数据它引用”,因为我们不确定someString 是对该数据的唯一引用。完成此操作的方法因语言和垃圾收集系统而异,但 ActionScript 中的基本处理是:每隔一段时间(特别是如果 Flash 当前占用大量内存),垃圾收集器会查看所有当前 SWF 中的对象并查找所有没有任何引用的对象 [4]。因此,对于我们之前的示例,如果 someString 是唯一引用字符串 "blah" 并且 someString 超出范围的对象,则 "blah" 不与任何标签关联,因此 Flash 完全无法访问。下次垃圾收集器运行时,它会找到它,然后向系统报告它已经完成了那块内存。

          现在,如果不查看源代码,很难说出您的特定问题的具体细节,但我可以告诉您,经常导致问题的一件事是事件侦听器。有时您可能会创建一个函数并将其添加为事件侦听器。现在事件本身有对该函数的引用 [5],所以如果函数超出范围,仍然有对它的引用,并且该函数不会被垃圾收集器拾取(记住, 函数是 ActionScript 中的对象)。您可以通过调用 removeEventListener 手动删除事件处理程序(我不推荐它),或者将调用中的 useWeakReference 参数设置为 addEventListener()true。垃圾收集器无法识别弱引用,因此将 useWeakReference 设置为 true 可以防止您有一个函数应该被垃圾收集器拾取,但它没有因为有人将它引用为事件侦听器。从项目中 mouseEvent 侦听器的绝对数量来看,这可能是罪魁祸首。如果你有 Flex Builder,你可以使用分析器查看你有多少方法闭包;如果他们上升并且永远不会下降,那可能是你的问题。

          [1]:请记住,这也适用于您在不使用 new 关键字的情况下创建对象的情况,例如您要编写 var exampleInt:int = 5;
          [2]:我选择 uint 是因为所有 uint 的大小都相同,所以我可以自信地说我知道它将是 4 个字节。字符串不同;它们占用的内存量与它们包含的字符数成正比,因此在这两个示例中都使用 String 会更加模棱两可。
          [3]:ActionScript 关键字delete 不是用于此目的,因此不要尝试以这种方式使用它,即使这样会合乎逻辑并且非常有用。
          [4]:Flash使用mark-and-sweep风格的策略,更具体的细节可以阅读here。 [5]:实际上,这是一个粗体的谎言,因为 addEventListener 方法使用了closures,但我对它的理解很薄弱,所以我不能把它归结为普通人的英语。

          【讨论】:

            【解决方案6】:

            只是为了帮助您了解为什么您以前没有听说过这一点,在以前的 AS 版本中,GC 与显示列表有很深的联系。一旦您从场景中移除了一个对象,它就不再存在(连同任何子对象),并且对它们的任何引用都变为 null。正如您可能已经知道的那样,这种行为在 AS3 中完全相反。祝你好运!

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2012-07-19
              • 1970-01-01
              • 2014-01-31
              • 1970-01-01
              • 2010-11-10
              • 1970-01-01
              相关资源
              最近更新 更多