Dart【09】垃圾回收

187 阅读3分钟

Dart 的垃圾收集器是分代的,由两个阶段组成:年轻空间清除器和并行标记清除收集器。

调度

为了最小化垃圾回收对APP和UI线程的影响,垃圾收集器向 Flutter 引擎提供了hooks来提醒它

当引擎检测到APP是闲置的或者没有用户行为,这使垃圾收集器有机会在不影响性能的情况下运行其收集阶段。

垃圾收集器还可以在这些空闲时间间隔内运行滑动压缩,通过减少内存碎片来最小化内存开销。

新生代

这个阶段设计用来清理生命周期较短的临时对象,例如无状态组件

虽然它是阻塞的,但它比第二代标记/扫描快得多,并且当与调度相结合时,几乎消除了应用程序运行时的感知暂停。

本质上,对象被分配到内存中的一个连续空间,当对象被创建时,它们被分配下一个可用空间,直到分配的内存被填满。 Dart 使用凹凸指针分配来快速分配新空间,使过程非常快。

分配新对象的空间由两半组成。 同一时间只使用一半:一个处于活跃状态,另一个处于非活跃状态。 新对象被分配到活跃的一半,一旦活跃空间被填满,活跃的对象就会从活跃区域复制到非活跃区域,原有的活跃区域非活跃对象就被忽略。 然后原有的不活跃区域变为活跃区域,之后不断重复这个过程。

检测当前代码中还在引用的对象,将它移动到活跃区域。

要确定哪些对象是否可被回收,收集器将以root对象(例如堆栈变量)开始,并检查它们引用的对象。然后把引用的对象移动到另一半空间。在那里它检查这些移动的对象指向的内容,并移动这些引用的对象。如此反复,直到移动所有活动对象到另一半空间。始终没有被引用的对象将被回收。

次生代

当对象生命周期较长时,它们会被移动到一个新的内存空间,由次生代垃圾回收器来管理。

这个垃圾回收器有两个阶段,

  1. 遍历所有对象,标记仍在使用的,所有的内存都会被扫描。
  2. 任何未标记的对象都会被清除,清除所有标记。

当次生代标记信号的时候,内存不可以变化,UI线程会被锁定。次生代收集器比较少见,因为短期对象由新生代垃圾收集器处理,但Dart runtime有时需要暂停来运行这种垃圾回收。鉴于 Flutter 能够调度收集,应该将其影响降到最低。

应该注意的是,如果一个应用程序不遵守弱世代假设(即大多数对象在年轻时死去),那么这种形式的收集将更频繁地发生。 鉴于 Flutter 的小部件实现是如何工作的,这不太可能,但需要牢记。

谨慎使用长驻内存的对象,因为这会频繁的引发次生代垃圾回收

Isolate

值得注意的是,Dart Isolate有自己的私有内存区块,彼此独立。 由于每个Isolate在单独的线程中运行,每个Isolate的垃圾收集事件不影响其他Isolate的性能。 使用隔离Isolate避免阻塞 UI 线程好方法。