方舟等级生成工具_方舟编译器简明学习笔记7—垃圾回收优化

7fccec558c7825a255bfc6c3050714bb.png

网上无参考,本文正确性待商榷

这部分内容我在网上找了很久,发现并没有人研究这部分内容,我只能再去翻看源码,以期望获得方舟编译器在垃圾回收这一块的优势所在。要学习这方面内容,首先需要大家了解垃圾回收机制中GC标记清除算法、RC算法以及相关的优化拓展算法。


预备知识—系统统一回收处理机制

标记清除算法

该算法是垃圾回收机制中最常见的,普及一下GC的基本单位——对象,对象一般包含头(header)和体(field),头包含了这个对象的定义信息,包括大小(size)、类型(type)和信息(info),体包括了这个对象的行为方法以及构造方法(构造方法就是产生对象用的)。对象可以是万事万物,后面我们把这个对象看做一个人,也可以是工具。

整体流程我用一个通俗的例子来说明,一个对象姑且定为一个人,当他开始使用其他工具进行工作的时候,这些工具就开始被打上标记,所以我们看到打标记的方法(mark函数)是遍历人所使用的元素。而人将这些工具使用完毕,解除了联系,但因为标记还在工具上,不属于被回收的内容,借用工具的系统仍不会回收。

当系统内存告急时,所有人先停止工作,把工具交出来检查一下,没有标记的全部都收缴了,有标记的继续留存,但是有些工具已经没人在用了,系统就想出一个办法,把所有有标记的全部清除标记,然后人在使用工具的时候再标记,下次系统回收的时候这些没被标记的,人们不用的工具就会被回收。

存在的问题:

1.STW(stop the world),就是全部人都要停止手头的工作,等着系统检查和回收工具,然后还要重新给工具上标记;

2.回收的内存是碎片化的,所有回收的工具都是零散状态,这边工具桌上一个,那个桌子上一个,清理一个完整的大桌子给一个新人用做不到。

更新拓展办法

1.标记整理,首先标记出所有需要回收的工具,然后让大家把工具统一放到几张桌子上再取用,然后把空出来的桌子清理掉。这样就不会产生内存空间碎片,但是整理会花一定的时间,比较适合清理长时间使用的工具,这样次数就不会太多。

2.复制算法和分代复制算法,因为每件工具使用的频度是不一样的,所以先将摆放工具的桌子全部分成两等分,一部分放现有的工具,另一部分空闲,当第一部分桌子不够用的时候,就将中间的这些带标记的在用工具复制一份到另一边的空闲的桌子上,这部分桌子全部清理,这样也不会产生内存碎片,只需要移动堆顶指针顺序分配内存,但是内存利用率低。

首先是提出了改进的复制算法,就是工具使用频度很大的情况下,经常需要换工具和开展回收工作,那么这些工具所需要的桌子也没必要整那么大,所以可以给一张大桌子,两张小桌子用就行了,一般设置比例为8:1最为合理。这样就提升了内存使用效率

然后提出了分代复制算法,分代指的是新生代和老年代,分别指的是使用频度大的工具和频度小但时间久的工具,分别针对这些工具的特点选用合适的算法,比如针对频度大的工具使用复制算法,针对频度小时间长的工具使用标记整理法。

3.延迟清除法,由于需要清理的桌子数目越多,回收花费的时间越多,这种情况下会导致停工时间过长而得不偿失。所以在标记操作结束后,所有的无标记工具可以暂时不回收,等到方便的时候再回收。缺点就是这些工具被延迟回收后,垃圾的分布不平均,因为有的工具使用频度大、很快就被释放了,导致有得桌子上满满的都是垃圾,有的桌子没什么垃圾。

预备知识—引用计数自行回收处理机制

RC算法

该算法可以通俗举例说一下,就是每个人配了一个计数器,这个人手头如果是有工具的说明在干活,有一个工具就在计数器上面加1,如果计数器上面计数是0,说明这个人不用干活了,这个人就自己溜达着离开厂房了。好处很明显,自己释放了自己的内存,把自己的工位空出来了,其他人想要使用这个工位直接就可以使用。

存在的问题:

但目前在垃圾回收机制中使用得并不常见,缺点很明显,一是在对象级上进行计数,每发生调用或释放工具就会引发计数,导致计数占用大量资源;二是循环引用计数问题不能克服,也就是两个对象互相引用,则二者计数永远是2,无法清零导致不能释放相应的内存资源,比如人和假牙互相嵌套,没法分离,但是这个人和他的假牙,对于系统而言都没有用了,却无法释放这个人占着的工位。三是计数器占用位宽很大,比如一个人是童工,也要用一个跟自身差不多大的计数器,这就使得他工位本来很小,因为计数器的存在也变得很大。

更新拓展办法:

1.延迟引用计数延:主要是为了解决增减计数过多的问题,办事是设置一个休息间,这些计数器为0的人把椅子(工位)搬过去坐在休息区休息,中途可能有些人计数器又变化了回到工位上了,等到需要的时候,统一把这些人的椅子回收了,放回工位上。缺点就是垃圾不能立即回收,损失了RC引用计数的一大优点。

2.sticky引用计数:主要是解决引用计数的计数器占用空间过大的问题,应用了GC标记 - 清除算法进行管理,从而将一些占用位宽比太大的计数器给省略掉用标记代替。

还有一些拓展方法,比如1位引用计数、部分标记引用计数等,就不拓展开来讲了。

方舟编译器垃圾回收机制

方舟编译器垃圾回收算法,在垃圾回收方面采用的是RC计数优化算法(计数参数是ref)加消除环算法(改进版--标记消除环)。

其实RC计数中最明显的问题应该是循环引用,一旦两个对象循环引用,其引用计数始终是2,无法归零回收。采用消除环算法(就是AB循环引用了,最后都要给AB对象赋值NULL,从根节点上走就碰不到循环引用无法回收的问题了)可以解决这个问题,不过方舟在这方面做得改进更加智能。

为避免内存被这些环的死循环占用,方舟引入了标记处理,就是对这些环进行管理和标注,就是在编程阶段,出现环问题时就要警告你这么做危险,这样子可以减少大部分程序中环的出现。另外一方面,方舟编译器在运行状态下引入了高效的环回收机制,允许有选择地智能回收某个APP的内存占用,对于传统环回收算法来说这是很强大的一个改进。

RC有关源代码内容

集中在me_rc_lowing源码中,整个流程包括了RC初始化(准备)、初始化检查、RC预降级、RC降级,然后执行所有剩余的RC方法,包括RC方法SSA构建、寄存RC降级结果,RC的降级工作就完成了。

总结该部分内容,计数降级步骤是基于预先分析结果生成一个计数参数赋值的本地RC函数。该本地函数在目标代码生成过程中会被降级以满足方舟编译器的IR层使用。

方舟编译器在Me_RClowing中对很多计数的内容进行了限定。

在me_rc_lowing中,大部分方法都是对计数进行一个情形限定,比如构造函数初始化一个对象后,相应的初始化映射要写入到该对象的field(对象域)内,如果域中检查没有发现这个映射,说明对象未被赋值,第一次赋值时RC不进行计数。

同时在symbol标识符这个源码文件下也发现了一些对RC计数限定的函数。

在mir_symbol中,也找到了RC有关限定的内容,比如一些基于字符串的虚指针、虚函数表指针、接口表指针、函数指针、类型表指针以及根函数等,要避免被RC计数,编写了一个忽略影响的函数。

从方舟当前公开的源码来看,有maple前端的部分,重点就是我上一篇mpl2mpl的部分内容,有SSA构建(就是静态单一赋值),这是一种优化方法,是一种后端寄存器分配算法,主要用于循环嵌套优化、过程间优化以及普通的函数内优化。还有就是RC插入和RC计数优化,本篇应该是探索了一部分,后续源码分析过程中如果再有发现,及时对这部分进行补充。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值