[编译原理]垃圾收集

这是《现代编译原理》中第二部分(高级主题)中的内容。个人感觉此章在副标题的结构上略有问题,应将本章主要内容分为两大部分:

  • 判断对象是否存活
  • 垃圾回收机制

概念

  • 垃圾:堆中分配且通过任何程序变量形成的指针都无法到达的记录称为垃圾

  • 垃圾收集由运行时系统完成

判断对象是否存活

标记-清扫式

在标记清扫式中,程序变量和指针构成一个有向图,用DFS对图进行遍历,标记可以到达的点,于是未被标记的点就是垃圾,应当被回收。从第一个地址到最后一个地址对整个堆进行清扫。清扫出来的垃圾用一个链表(空闲表)链接在一起。

代价

令堆的大小为H,可到达数据为R,则未到达数据为R-H.(小写字母代表常数)。
清扫所需时间与堆的大小成正比,一次垃圾收集的代价可用aR+bH表示,回收得到的H-R是回收得到的好处,于是,分摊代价就是:

aR+bHHR

书中还提到优化,对于DFS的优化就是将递归算法(递归深度最坏为H)改为非递归算法,并用指针逆转(暂时用不到的、其内容已被存储过的指针指向某个有用的数据,在适当时候返还其原来的值给它)来进行优化。

  • 空闲表的优化:思想是用不同大小的内存块群来存储相应大小的内容
  • 碎片:外部碎片和内部碎片,外部碎片就是有很多没用到的很小的不能利用的记录(windows中的磁盘整理就是整理这些碎片),内部碎片就是说分配的内存大于实际用到的,造成内部碎片的产生。

引用计数

思想

每次将p存储到一个指针时便增加p的引用计数(每次类似的赋值操作需要多出好多条指令来完成这样的计数工作)。

问题

  • 无法回收环形垃圾
  • 增加指令的开销很大

垃圾回收的具体机制

复制式收集

堆中有两块区域from-spaceto-space,将不是随便的对象从from-space复制到to-space。于是不会产生碎片问题。

  • 引用局部性:如果一个位于地址a的记录指向另一个位于地址b的记录,则a和b可能相距很远。

再有告诉缓存的计算机中,具有良好的局部引用性非常重要,当程序读取了地址a的数据后,会预期不久将读取a附近的数据。此时深度优先遍历广度优先遍历(书中叫“宽度优先遍历”)的性能就差别很大了,在深度优先遍历中父亲节点往往与其孩子节点相邻较近,故其局部引用性比较好。

分代收集

思想

根据对象的活跃度将对象氛围几个“代(G)”,年轻的会慢慢变老哦。
收集器会更多得关注年轻的代(其成为垃圾的可能性更高)。

问题

年老的指向年轻的,这样光对年轻代扫描得到的数据不准确。
解决方案

  • 记忆表:用向量记录更新过的对象
  • 记忆集合:用对象内的一位来记录是否更新过
  • 卡片标记:划分存储区
  • 页标记:利用操作系统中的脏位

增量式收集

提供了良好的交互性。不多展开了。
其中提到两个概念,在并发编程中很常见(类似于《java并发编程实战》中提到的先验后验):

  • 栅栏写:每一条存数指令进行检查以确保其遵守相关不变式。
  • 栅栏读:每一条度数指令进行检查以确保其遵守相关不变式。

在JAVA中的应用

主流java虚拟机使用可达性分析(标记-清扫)来判断内存是否是垃圾。
java中的对象被宣告”正式死亡“需要经历两次标记,第一次标记的时候会进行筛选,筛选的条件是是否有必要执行finalize方法。如果是,则将这个对象放入F-Queue中。第二次是对F-Queue中的对象进行标记,此时调用对象的finalize方法(如果此时对象将自己和任何一个对象建立关联即可逃脱被“杀死”的命运),然后对象就一命呜呼了。

建议不要使用finalize方法而改用try-finally之类的方法。

java中使用分代收集的方法来对垃圾进行回收。新生代使用复制式收集老年代使用标记-清理标记-清扫。其中新生代的内存区域分为两个Survivor区和一个Eden区,对象存储在一个Surivor区和Eden区,垃圾回收之后放入另一个Surivor区。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
编译原理中的正则定义常量是指使用正则表达式来描述编译原理中所需的常量。正则表达式是一种定义字符串模式的强大工具,它可以用来匹配、搜索、替换字符串。在编译原理中,正则表达式被广泛应用于词法分析阶段,用来定义词法单元的模式。 在编译器中,常量是指程序中固定不变的值,例如整数、浮点数、字符串等。通过编写正则表达式来定义常量,可以方便地在词法分析中将它们识别出来,并生成对应的词法单元。正则表达式中的特殊符号和操作符可以用来描述常量的模式,例如使用\d+匹配整数,使用[0-9]+(\.[0-9]+)?匹配浮点数,使用\".*\"匹配字符串等。 编译原理中的正则定义常量编译原理的具体步骤如下: 1. 分析语言中常量的模式和规律,确定需要定义的常量类型。 2. 使用正则表达式来描述每个常量类型的模式,使用特殊符号和操作符来表示常量的组成部分和限制条件。 3. 将每个常量类型的正则表达式编译成有限自动机(DFA)或非确定有限自动机(NFA)。 4. 在词法分析阶段,将输入的源代码按照正则表达式定义的常量类型进行分割和识别,生成对应的词法单元。 5. 将识别出的词法单元作为输入传递给下一个编译阶段,如语法分析、语义分析等。 总之,编译原理中使用正则定义常量的方法可以方便地识别和处理语言中的常量,并为后续的编译工作提供基础。通过适当定义和使用正则表达式,可以提高编译器的效率和准确性。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值