阅读 1177

Flutter中的垃圾回收机制

1.介绍

Flutter主要使用Dart开发语言,在调试和发布两个版本中,Dart RunTime是始终存在,但两种版本下的构建方式有很大的差异

2.调试和发布版本下的差异

  • 调试版本下 Dart编译到设备,包含三部分:

    1.Dart RunTime

    2.jit(Android下的实时编译器)/interpreter(IOS下的解析器)

    3.调试和分析服务

  • 发布版本下

    1.Dart RunTime

两种模式下都存在Dart RunTime,它包含了垃圾收集器,是实例化对象并变得无法访问时分配和释放内存的必要组件。

3.垃圾收集器竞技场

对于Flutter而言,会创建很多对象:例如Stateless Widget从创建到应用程序的状态发生改变或者变得不再可见时被销毁和重建,大多数对象的生命周期是短暂的,若应用程序的UI变得相对复杂,可运行至上千个小部件

对于上面而言,很多人之前认为Flutter为什么不用Java写,为什么不用Object-C写,为什么不用JavaScript写,对于这些语言真的能胜任这么频繁的创建销毁吗?

  • Java垃圾收集器

    • jvm中java的内存分为四个部分:

      1.Java栈:主要作用存放方法执行的时候所有的数据,由栈帧代表一个方法的执行,每个方法从调用到执行完成在虚拟机为一个栈帧的入栈和出栈,栈帧的信息包括局部变量表,栈操作数,动态链接,方法出口

      2.本地方法栈:主要为native服务,例如C、C++方法

      3.方法区:存储被虚拟机加载的类信息、常量、静态变量、即使编译器编译后的数据等

      4.堆区:所有通过new创建的对象的内存都在堆中分配,堆内存分为新的和旧的,刚new出来的对象放在新生代存储,当内存不足时,虚拟机会通过一系列算法把新生对象移动到旧生代中去

    • 注意:

      1.当方法栈深度大于JVM深度的时候,就会栈溢出,例如:死循环(stackOverflow)

      2.新生代和旧生代都满了,就会导致内存溢出(OutOfMemory)

    • 垃圾收集器的算法

      垃圾回收主要针对堆内存,算法主要包括垃圾的确定与收集、垃圾的回收、垃圾的回收时机

      1.引用计数法(废弃):若对象被引用就会+1,没有被引用的时候就回收,但引用计数法无法解决对象之间相互调用的问题

      2.可达性算法:通过gc root对象开始搜索,不可达的对象会被回收,引用的类型主要有强引用、弱引用,当存在强引用时宁愿抛出oom也不回收、但是弱引用的话,有可能被回收。

      3.标记清除法:搜索发现没有引用的对象直接回收,但是导致碎片过多

      4.复制算法:搜索扫描没有引用的对象,开辟新的内存空间,将存活的对象复制到新的内存,旧的内存直接删除,由于交换空间,适合对象比较少的时候,并且内存空间缩短一半

      5.标记整理法:在标记清除法的基础上,清除掉不存活的对象,把后面存活的对象挪动过来,解决碎片问题

    • 上面的垃圾收集器算法在jvm中没有明确的规范,由各个厂商去实现

  • Object-C垃圾收集器

OC在早期版本中缺少较为完善的内存管理机制,需要开发者手动进行释放,在Xcode4.2之后引入了ARC(Automatic Reference Counting)机制。

  • ARC机制

    ARC叫做自动引用计数,ARC中常见的所有权关键字:

    • assign 对应关键字__unsafe_unretained,指向的对象被释放的时候,仍然指向之前的地址,容易引起野指针
    • copy 对应关键字__strong,在赋值的时候,调用copy方法
    • retain 对应关键字__strong
    • strong 对应关键字__strong
    • unsafe_unretained 对应关键字unsafe_unretained
    • weak 对应关键字weak
  • ARC内部实现

    ARC背后的引用计数主要依赖于三个方法:

    • retain 增加引用计数
    • release 降低引用计数,当引用计数为0时释放对象
    • autorelease 在当前的auto release pool结束后,降低引用计数
  • JavaScript垃圾收集器

javaScript 具有垃圾自动收集机制,垃圾收集器会按照固定的时间间隔,周期性地执行这一炒作,具体到浏览器的实现,也可以指定收集时间

  • 垃圾收集的方法

    • 标记清除法 javaScript中最重要的收集方法,给当前不使用的值加上标记,然后等待回收其内存

    • 引用计数(不再使用)

    • 性能问题 垃圾收集器是周期运行的,而且如果变量分配的内存数量比较大,那么回收工作量也是相当的大

  • Dart垃圾收集器

Dart的垃圾收集器是分代的,由两个部分组成:新生代空间收集器、并行标记扫描收集器,还有一个重要的东西,就是调度器

  • 调度器

    在Flutter引擎中,为了最小化垃圾收集对应用程序和UI性能的印象,与垃圾收集器提供了hook当引擎检测到应用程序处于空闲状态(没有与用户交互)会发出警报,为垃圾收集器提供运行其收集阶段而不影响性能的机会。并且垃圾收集器可以在这些空闲时间运行内存压缩,从而较少内存碎片来优化内存

  • 新生代空间收集器

    此部分类似于Java的复制算法,用于清理寿命较短的对象,例如Stateless部件,虽然是会阻塞线程,但当与调度器结合使用,几乎感知不到应用程序在运行期间的暂停,从本质上,新建的对象被分配给内存中的连续空间,在新建对象,会被分配到下一个可用空间,直到填充完分配的内存,但Dart使用的是一个凹凸的指针,所以这个过程非常快,分配新对象的空间由两部分组成,任何时候只用一半,当一半满后,活动的对象将复制到另一半空间中,一半就会全部清空,确定对象是否活动,收集器以根对象开始,进行检测他们引用的内容,这一部分类似于Java可达性算法,有引用的对象将会被复制到另一个空间中

  • 并行标记扫描收集器

    当对象达到一定的生命周期时,会被提上到另一个新的内存空间,由另一个收集器管理,此收集器有两个阶段:

    • 遍历对象,标记仍在使用的对象

    • 扫描整个存储器,并回收未标记的对象,然后清除所有标记

4.总结

由上面所述,Dart的垃圾收集器方式参考了部分语言的实现,但需要注意的是,Dartisolates拥有自己的私有堆,彼此是独立的,每个isolates运行在单独的线程中,每个ioslates的垃圾收集事件不影响其它isolates的性能,所以isolates可以避免UI出现卡顿和很好的进行频繁的回收操作,这就是dart作为Flutter的主要语言的原因之一。

参考: 1.Flutter: Don’t Fear the Garbage Collector

关注下面的标签,发现更多相似文章
评论