编译原理 编译系统和运行系统 11_第1页
编译原理 编译系统和运行系统 11_第2页
编译原理 编译系统和运行系统 11_第3页
编译原理 编译系统和运行系统 11_第4页
编译原理 编译系统和运行系统 11_第5页
已阅读5页,还剩60页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

1、第十一章第十一章 编译系统和运行系统编译系统和运行系统 本章内容本章内容 c语言编译系统语言编译系统 预处理器、汇编器预处理器、汇编器、连接器连接器 目标文件的格式、静态库、动态连接目标文件的格式、静态库、动态连接 java运行系统运行系统 无用单元收集(垃圾收集)无用单元收集(垃圾收集) 掌握从源程序到可执行目标程序的实际处理掌握从源程序到可执行目标程序的实际处理 过程过程 对实际参与软件开发是直接有用的对实际参与软件开发是直接有用的 11.1 c语言语言编译系统编译系统 预处理器预处理器 源程序源程序 修改后的源程序修改后的源程序 可重定位的目标程序可重定位的目标程序 可重定位的可重定位的

2、 目标文件目标文件 库库 编译器编译器 汇编器汇编器 汇编程序汇编程序 连接器连接器 可执行的目标程序可执行的目标程序 c源程序可以分成源程序可以分成 若干个模块若干个模块 分别进行预处理分别进行预处理 、编译和汇编、形、编译和汇编、形 成可重定位的目标成可重定位的目标 文件文件 目标文件和必要目标文件和必要 的库文件连接成一的库文件连接成一 个可执行的目标文个可执行的目标文 件件 gccgcc和和cccc是编译驱是编译驱 动程序的名字动程序的名字 11.1 c语言语言编译系统编译系统 main.c (1) #if 1 (2) int buf2; (3) #else (4) int buf2

3、= 10,20; (5) #endif (6) void swap(); (7) #define a buf0 (8) int main() (9) (10)scanf(%d, %d, buf, buf+1); (11)swap(); (12)printf(%d, %d,a, buf1); (13)return 0; (14) swap.c (1) extern int buf2; (2) int *bufp0 = buf; (3) int *bufp1; (4) void swap() (5) (6) int temp; (7)bufp1 = buf+1; (8)temp = *bufp0;

4、 (9)*bufp0 = *bufp1; (10)*bufp1 = temp; (11) 11.1 c语言语言编译系统编译系统 11.1.1 预处理器预处理器 gcc首先调用首先调用预处理器预处理器cpp,将源程序文件翻将源程序文件翻 译成一个译成一个ascii中间文件,它是经修改后的源中间文件,它是经修改后的源 程序程序 cpp实现以下功能实现以下功能 文件包含文件包含 宏展开宏展开 条件编译条件编译 11.1 c语言语言编译系统编译系统 main.c (1) #if 1 (2) int buf2; (3) #else (4) int buf2 = 10,20; (5) #endif (6)

5、 void swap(); (7) #define a buf0 (8) int main() (9) (10)scanf(%d, %d, buf, buf+1); (11)swap(); (12)printf(%d, %d,a, buf1); (13)return 0; (14) main.i (1) # 1 “main.c” (2) (3) int buf2; (4) (5) (6) (7) void swap(); (8) (9) int main() (10) (11) scanf(%d, %d, buf, buf+1); (12) swap(); (13) printf(%d,%d,

6、buf0, ); (14) return 0; (15) 11.1 c语言语言编译系统编译系统 11.1.2 汇编器汇编器 gcc系统的编译器系统的编译器cc1产生汇编代码产生汇编代码 最简单的汇编器对输入进行两遍扫描最简单的汇编器对输入进行两遍扫描 一遍扫描完成汇编代码到可重定位目标代码一遍扫描完成汇编代码到可重定位目标代码 的翻译也是完全可能的的翻译也是完全可能的 用用 gcc s main.c 可以得到汇编文件可以得到汇编文件main.s 用用 as o main.o main.s 可以将可以将main.s汇编成可重定位目标文件汇编成可重定位目标文件main.o 11.1 c语言语言编译

7、系统编译系统 一段汇编代码一段汇编代码 .l2: cmpl $0,-4(%ebp) jne .l6 jmp .l11 .l11: cmpl $0,-8(%ebp) jne .l6 jmp .l12 .l12: jmp .l5 .p2align 4,7 .l6: 11.1 c语言语言编译系统编译系统 11.1.3 连接器连接器 目标模块或目标文件的形式目标模块或目标文件的形式 可重定位的目标文件可重定位的目标文件 可执行的目标文件可执行的目标文件 共享目标文件共享目标文件 一种特殊的可重定位目标文件一种特殊的可重定位目标文件 在装入程序或运行程序时,动态地装入到内存并在装入程序或运行程序时,动态

8、地装入到内存并 连接连接 11.1 c语言语言编译系统编译系统 连接连接是一个收集、组织程序所需的不同代码是一个收集、组织程序所需的不同代码 和数据的过程,以便程序能被装入内存并被和数据的过程,以便程序能被装入内存并被 执行执行 连接的时机连接的时机 编译时编译时 装入时装入时 运行时运行时 静态连接器静态连接器 动态连接器动态连接器 11.1 c语言语言编译系统编译系统 一个重定位模块一个重定位模块m可能可能定义和引用的符号定义和引用的符号 全局符号全局符号 指那些在模块指那些在模块m中定义,可以被其它中定义,可以被其它 模块引用的符号模块引用的符号 局部符号局部符号 指那些在模块指那些在模

9、块m中定义,且只能在本中定义,且只能在本 模块中引用的符号模块中引用的符号 外部符号外部符号 指那些由模块指那些由模块m引用并由其它模块定引用并由其它模块定 义符号义符号 符号解析符号解析 识别各个目标模块中定义和引用的符号,为每一识别各个目标模块中定义和引用的符号,为每一 个符号引用确定它所关联的一个同名符号的定义个符号引用确定它所关联的一个同名符号的定义 重定位重定位 11.1 c语言语言编译系统编译系统 11.1.4 目标文件的格式目标文件的格式 目标文件格式随系统不同而不同目标文件格式随系统不同而不同 介绍介绍unix的的elf(executable and linkable form

10、at)格式格式 linux、system v unix的后期版本、的后期版本、bsd unix变体和变体和sun solaris,都使用都使用unix的的elf 格式格式 11.1 c语言语言编译系统编译系统 elf头头 描述了字的大小描述了字的大小 产生此文件的系统的字产生此文件的系统的字 节次序节次序 目标文件的类型目标文件的类型 机器类型机器类型 节头表的位置、条目多节头表的位置、条目多 少少 其它其它 elf头头 .text .rodata .data .bss .symtab .rel.text .rel.data .debug .line .strtab 节节头头表表 0 描述目标

11、文描述目标文 件的节件的节 节节 11.1 c语言语言编译系统编译系统 节头表节头表 描述目标文件中各节的描述目标文件中各节的 位置和大小位置和大小 处于目标文件的末尾处于目标文件的末尾 elf头头 .text .rodata .data .bss .symtab .rel.text .rel.data .debug .line .strtab 节节头头表表 0 描述目标文描述目标文 件的节件的节 节节 11.1 c语言语言编译系统编译系统 .text节节 被编译程序的机器代码被编译程序的机器代码 .rodata节节 诸如诸如printf语句中的格语句中的格 式串和式串和switch语句的跳语

12、句的跳 转表等只读数据转表等只读数据 .data节节 已初始化的全局变量已初始化的全局变量 elf头头 .text .rodata .data .bss .symtab .rel.text .rel.data .debug .line .strtab 节节头头表表 0 描述目标文描述目标文 件的节件的节 节节 11.1 c语言语言编译系统编译系统 .bss节(节(.comm 节)节) 未初始化的全局变量未初始化的全局变量 在目标文件中不占实际在目标文件中不占实际 的空间的空间 .symtab节节 记录在该模块中定义和记录在该模块中定义和 引用的函数和全局变量引用的函数和全局变量 的信息的符号表

13、的信息的符号表 elf头头 .text .rodata .data .bss .symtab .rel.text .rel.data .debug .line .strtab 节节头头表表 0 描述目标文描述目标文 件的节件的节 节节 11.1 c语言语言编译系统编译系统 .symtab节节 type func object bind global local extern elf头头 .text .rodata .data .bss .symtab .rel.text .rel.data .debug .line .strtab 节节头头表表 0 描述目标文描述目标文 件的节件的节 节节 1

14、1.1 c语言语言编译系统编译系统 .symtab节节 name value 偏移偏移地址,或地址,或 绝对绝对地址地址 size 字节数字节数 elf头头 .text .rodata .data .bss .symtab .rel.text .rel.data .debug .line .strtab 节节头头表表 0 描述目标文描述目标文 件的节件的节 节节 11.1 c语言语言编译系统编译系统 .rel.text节节 .text节节中需要修改的中需要修改的 单元的位置列表单元的位置列表 .rel.data节节 用于被本模块引用或定用于被本模块引用或定 义的全局变量的重定位义的全局变量的重

15、定位 信息信息 elf头头 .text .rodata .data .bss .symtab .rel.text .rel.data .debug .line .strtab 节节头头表表 0 描述目标文描述目标文 件的节件的节 节节 11.1 c语言语言编译系统编译系统 .debug节节 用于调试程序的调试符用于调试程序的调试符 号表号表 .line节节 源文件和源文件和.text节中的机节中的机 器指令之间的行号映射器指令之间的行号映射 .strtab 一组有空结束符的串构一组有空结束符的串构 成的串表成的串表 elf头头 .text .rodata .data .bss .symtab

16、.rel.text .rel.data .debug .line .strtab 节节头头表表 0 描述目标文描述目标文 件的节件的节 节节 11.1 c语言语言编译系统编译系统 11.1.5 符号解析符号解析 将每个符号引用正确地与某可重定位模块的将每个符号引用正确地与某可重定位模块的 符号表中的一个符号定义相关联,从而确定符号表中的一个符号定义相关联,从而确定 各个符号引用的位置各个符号引用的位置 在所有输入模块中都找不到被引用符号的定在所有输入模块中都找不到被引用符号的定 义,则打印错误消息并结束连接义,则打印错误消息并结束连接 需要定义解析规则需要定义解析规则 11.1 c语言语言编译

17、系统编译系统 解析规则解析规则 函数和已初始化的全局变量称为强符号;未函数和已初始化的全局变量称为强符号;未 初始化的全局变量称为弱符号初始化的全局变量称为弱符号 不允许有多重的强符号定义不允许有多重的强符号定义 出现一个强符号定义和多个弱符号定义时,出现一个强符号定义和多个弱符号定义时, 选择强符号的定义选择强符号的定义 出现多个弱符号定义时,选择任意一个弱符出现多个弱符号定义时,选择任意一个弱符 号的定义号的定义 11.1 c语言语言编译系统编译系统 11.1.6 静态库静态库 将相关的可重定位目标模块打包成一个文件将相关的可重定位目标模块打包成一个文件, , 作为连接器的输入作为连接器的

18、输入 连接器仅复制库中被应用程序引用的模块连接器仅复制库中被应用程序引用的模块 gcc c swap.c编译编译 ar rcs mylib.a swap.o建库建库 gcc static o swap1 main.c /usr/lib/libc.a mylib.a 生成可执行文件生成可执行文件 11.1 c语言语言编译系统编译系统 printf.o等等 可重定位文件可重定位文件 翻译器翻译器 main.c源文件源文件 连接器连接器 main.o mylib.a swap.o libc.a 静态库静态库 swap1 完全连接的可执行文件完全连接的可执行文件 和静态库连接和静态库连接 11.1 c

19、语言语言编译系统编译系统 11.1.7 可执行目标文件及装入可执行目标文件及装入 可执行目标文件可执行目标文件与可重定位目标文件格式类与可重定位目标文件格式类 似似 可执行目标文件的装入由加载器完成可执行目标文件的装入由加载器完成 11.1 c语言语言编译系统编译系统 读读/ /写内存段写内存段 elf头头 段头表段头表 .init .text .rodata .data .bss .symtab .debug .line .strtab 节头表节头表 只读内存段只读内存段 符号表和调试信符号表和调试信 息,不装入内存息,不装入内存 描述目标文件描述目标文件 的节的节 将下面的节映射到将下面的

20、节映射到 运行时的内存段运行时的内存段 典型的典型的elf可执行目标文件可执行目标文件 11.1 c语言语言编译系统编译系统 linux运行时的内存映像运行时的内存映像 内核内核 用户栈用户栈 (运行时创建)(运行时创建) 共享库的共享库的 内存区域内存区域 运行时的堆运行时的堆 (运行时用(运行时用malloc创建)创建) 读读/写段写段 (.data, .bss) 只读段只读段 (.init, .text, .rodata) 未使用未使用 0 xc0000000 0 x40000000 0 x08048000 对用户代码对用户代码 不可见不可见 %esp (栈指针)(栈指针) brk 从可

21、执行文件从可执行文件 装入装入 0 11.1 c语言语言编译系统编译系统 这里描述的装入过程从概念上来说是正确的这里描述的装入过程从概念上来说是正确的 若需要了解装入过程真正是怎样工作的,必若需要了解装入过程真正是怎样工作的,必 须在理解了进程、虚拟内存和内存分页等概须在理解了进程、虚拟内存和内存分页等概 念以后念以后 11.1 c语言语言编译系统编译系统 11.1.8 动态连接动态连接 静态库静态库 周期性地被维护和更新周期性地被维护和更新 内存可能有多份内存可能有多份printf和和scanf的代码的代码 共享库共享库 在运行时可以装到任意的内存位置,被内存中在运行时可以装到任意的内存位置

22、,被内存中 的进程共享的进程共享 11.1 c语言语言编译系统编译系统 共享库以两种不同的方式被共享共享库以两种不同的方式被共享 共享库的代码和数据被所有引用该库的可执共享库的代码和数据被所有引用该库的可执 行目标文件所共享行目标文件所共享 共享库的共享库的.text节在内存中的一个副本可以被节在内存中的一个副本可以被 正在运行的不同进程共享正在运行的不同进程共享 11.1 c语言语言编译系统编译系统 可重定位文件可重定位文件 翻译器翻译器 (cpp,cc1,as) main.c源文件源文件 连接器(连接器(ld) main.o libc.so mylib.so 重定位和符重定位和符 号表信息

23、号表信息 部分连接的可执部分连接的可执 行目标文件行目标文件 swap2 加载器(加载器(execve) libc.so mylib.so 动态连接器(动态连接器(ld-linux.so) 代码和代码和 数据数据 此时,动态连接器是内存中此时,动态连接器是内存中 已完全连接的可执行代码已完全连接的可执行代码 11.1 c语言语言编译系统编译系统 加载器通常装入和运行动态连接器加载器通常装入和运行动态连接器 动态连接器接着完成连接任务动态连接器接着完成连接任务 把把libc.so的文本和数据装入内存并进行重定位的文本和数据装入内存并进行重定位 把把mylib.so的文本和数据装入内存并进行重定的

24、文本和数据装入内存并进行重定 位位 重定位重定位swap2中任何对中任何对libc.so或或mylib.so定义定义 的符号的引用的符号的引用 将控制传递给应用程序将控制传递给应用程序 11.1 c语言语言编译系统编译系统 11.1.9 处理目标文件的一些工具处理目标文件的一些工具 ar创建静态库,插入、删除、罗列和提取成创建静态库,插入、删除、罗列和提取成 strings 列出包含在目标文件中的所有可打印串列出包含在目标文件中的所有可打印串 strip 从一个目标文件中删除符号表信息从一个目标文件中删除符号表信息 nm 列出一个目标文件的符号表中定义的符号列出一个目标文件的符号表中定义的符号

25、 size 列出目标文件中各段的名字和大小列出目标文件中各段的名字和大小 readelf 显示目标文件的完整结构,包括编码在显示目标文件的完整结构,包括编码在elf头头 中的所有信息。它包括了中的所有信息。它包括了size和和nm的功能的功能 objdump可以显示目标文件中的所有信息。其最有可以显示目标文件中的所有信息。其最有 用的功能是反汇编用的功能是反汇编.text节中的二进制指令节中的二进制指令 ldd列出可执行目标文件在运行时需要的共享库列出可执行目标文件在运行时需要的共享库 11.2 java语言的运行系统语言的运行系统 java语言语言 简单性、分布性、安全性、可移植性等简单性、

26、分布性、安全性、可移植性等 我们关心的是:平台无关性我们关心的是:平台无关性 java虚拟机技术是实现虚拟机技术是实现java平台无关性特点平台无关性特点 的关键的关键 java运行系统就是运行系统就是java虚拟机的一个实现虚拟机的一个实现 11.2 java语言的运行系统语言的运行系统 11.2.1 java虚拟机语言简介虚拟机语言简介 java程序首先由程序首先由java编译器把它编译成字节码编译器把它编译成字节码, , 也就是也就是jvml程序程序 常量池常量池: : 各种字符串常量各种字符串常量, ,类似于传统程序设类似于传统程序设 计语言中的符号表计语言中的符号表 类成员信息类成员

27、信息: : 域信息表和方法信息表域信息表和方法信息表 jvml指令序列指令序列 11.2 java语言的运行系统语言的运行系统 java源程序中的方法源程序中的方法 int calculate (int i) int j =2; return (i+j) (j-1); 对应的字节码程序:对应的字节码程序: int calculate (int i) iconst_2 istore_2 iload_1 iload_2 iadd iload_2 iconst_1 isub imul ireturn 11.2 java语言的运行系统语言的运行系统 11.2.2 java虚拟机虚拟机 java虚拟机一

28、般由以下几个部分构成:虚拟机一般由以下几个部分构成: 类加载器(字节码验证器)类加载器(字节码验证器) 解释器或解释器或/和编译器和编译器 包括无用单元收集器和线程控制模块在内的包括无用单元收集器和线程控制模块在内的 运行支持系统,运行支持系统, 另外还有一些标准类和应用接口的另外还有一些标准类和应用接口的class文件文件 库库 java源源 程序程序 java字字 节码节码 java编编 译器译器 java字字 节码节码 在网络在网络 上移动上移动 java虚拟机虚拟机 类加载器(字节类加载器(字节 码验证)码验证) java类库类库 字节码解释器字节码解释器即时编译器即时编译器 无用单元

29、收集器无用单元收集器线程线程/ /同步机制同步机制 运行时支持运行时支持 linuxwin32/nt 硬件设备硬件设备 solaris 11.2 java语言的运行系统语言的运行系统 c语言语言 数据栈数据栈用来存放生存期和过程一次活动的用来存放生存期和过程一次活动的 生存期一致的局部变量生存期一致的局部变量 数据堆数据堆用来存放生存期和过程一次活动的用来存放生存期和过程一次活动的 生存期不一定一致的动态变量生存期不一定一致的动态变量 程序员通过程序员通过malloc和和free函数参与堆的管理函数参与堆的管理 不安全的一个根源(悬空指针、内存泄漏)不安全的一个根源(悬空指针、内存泄漏) ja

30、va语言语言 数据栈数据栈除对象和数组外,都分配在栈上除对象和数组外,都分配在栈上 数据堆数据堆对象和数组分配在堆上对象和数组分配在堆上 出于安全的要求,程序员不参与堆管理出于安全的要求,程序员不参与堆管理 11.2 java语言的运行系统语言的运行系统 无用单元收集(俗称垃圾收集)无用单元收集(俗称垃圾收集) 无用单元(理论上)无用单元(理论上) 那些在继续运行过程中不会再使用的数据单元那些在继续运行过程中不会再使用的数据单元 收集器采用稳妥策略收集器采用稳妥策略 实际上并非总能判断出一个数据记录的值以后实际上并非总能判断出一个数据记录的值以后 是否还需要是否还需要 通过根集(通过根集(ro

31、ots set,在栈上)以及从根集开始,在栈上)以及从根集开始 的可达性来定义变量的活跃性的可达性来定义变量的活跃性 无用单元(实现上)无用单元(实现上) 通常指那些不可能从程序变量经指针链到达的通常指那些不可能从程序变量经指针链到达的 堆分配记录堆分配记录 11.2 java语言的运行系统语言的运行系统 11.2.3 即时编译器即时编译器 当一个类的某个方法第一次被调用时,虚拟当一个类的某个方法第一次被调用时,虚拟 机才激活即时编译器将它编译成机器代码机才激活即时编译器将它编译成机器代码 生成的代码的执行速度可以达到解释执行的生成的代码的执行速度可以达到解释执行的 10倍倍 但是执行过程不得

32、不等待编译的结束,因此但是执行过程不得不等待编译的结束,因此 使得执行时间变长使得执行时间变长 很多虚拟机都会使用快速解释器和优化编译很多虚拟机都会使用快速解释器和优化编译 器的组合或者是简单编译器和复杂编译器的器的组合或者是简单编译器和复杂编译器的 组合组合 11.2 java语言的运行系统语言的运行系统 即时编译即时编译 对象引用对象引用方法表指针方法表指针 实例数据实例数据 方法表方法表 类指针类指针 方法方法0代码指针代码指针 方法方法1代码指针代码指针 compile-me代码段代码段 编译得到的机器编译得到的机器 代码代码( (本地代码本地代码) ) 即时编译器即时编译器 11.2

33、 java语言的运行系统语言的运行系统 重编译机制重编译机制 字节码字节码 未优化未优化 机器代码机器代码 优化机优化机 器代码器代码 快速代码生快速代码生 成的编译器成的编译器 优化编译器优化编译器 计数器计数器 统计数据统计数据 11.3 无用单元收集无用单元收集 无用单元收集器需要根据数据的活跃性来判断哪无用单元收集器需要根据数据的活跃性来判断哪 些是无用单元些是无用单元 活跃性分析采用稳妥策略,是通过根集以及从根活跃性分析采用稳妥策略,是通过根集以及从根 集开始的可达性来定义活跃性集开始的可达性来定义活跃性 因此从实现的角度,无用单元是那些不可能从程因此从实现的角度,无用单元是那些不可

34、能从程 序变量经指针链到达的堆分配记录序变量经指针链到达的堆分配记录 无用单元收集可能需要来自编译器、操作系统和无用单元收集可能需要来自编译器、操作系统和 硬件方面的支持硬件方面的支持 本节简要介绍一些主要的无用单元收集方法,并本节简要介绍一些主要的无用单元收集方法,并 且描述编译器和收集器之间的一些相互影响且描述编译器和收集器之间的一些相互影响 11.3 无用单元收集无用单元收集 11.3.1 标记和清扫标记和清扫 方法概述:首先标记堆上所有可达记录,然方法概述:首先标记堆上所有可达记录,然 后回收未被标记的记录后回收未被标记的记录 根集包含了全局变量、活动记录栈中的局部变量根集包含了全局变

35、量、活动记录栈中的局部变量 和被活跃着的过程使用的寄存器和被活跃着的过程使用的寄存器 堆上活跃记录的集合是从根集开始的任何一条指堆上活跃记录的集合是从根集开始的任何一条指 针路径上的记录的集合针路径上的记录的集合 任何图遍历算法,都可用于标记所有的可达记录任何图遍历算法,都可用于标记所有的可达记录 清扫从堆的首地址开始清扫从堆的首地址开始, 逐个记录地考察整个堆逐个记录地考察整个堆, 寻找未被标记的记录寻找未被标记的记录, 把它们链成一个空闲链表把它们链成一个空闲链表 11.3 无用单元收集无用单元收集 11.3.1 标记和清扫标记和清扫 传统的标记和清扫方法的问题传统的标记和清扫方法的问题

36、碎片问题:当要分配一个碎片问题:当要分配一个n字节大小的记录时,字节大小的记录时, 发现有很多小于发现有很多小于n字节的空闲块存在,但就是没字节的空闲块存在,但就是没 有合适的空闲块可分配给这个记录有合适的空闲块可分配给这个记录 引用局部性问题:使用块和空闲块相互交织,使引用局部性问题:使用块和空闲块相互交织,使 得当前要使用的各个活跃记录被分散到很多的虚得当前要使用的各个活跃记录被分散到很多的虚 拟内存页中,这些页在内存中可能被频繁地换进拟内存页中,这些页在内存中可能被频繁地换进 换出换出 11.3 无用单元收集无用单元收集 11.3.2 引用计数引用计数 方法概述:通过记住有多少指针指向每

37、个记方法概述:通过记住有多少指针指向每个记 录来直接完成标记,引用计数存在各记录中录来直接完成标记,引用计数存在各记录中 编译器需要在每个出现指针存储的地方生成额外编译器需要在每个出现指针存储的地方生成额外 的指令,以调整一些引用计数器的值的指令,以调整一些引用计数器的值 当一个记录的引用计数值为当一个记录的引用计数值为0的时候,就可以把的时候,就可以把 该记录加入空闲链表该记录加入空闲链表 被回收记录本身的指针域都要一一检查,它们所被回收记录本身的指针域都要一一检查,它们所 指向的记录的引用计数值也都要减指向的记录的引用计数值也都要减1 11.3 无用单元收集无用单元收集 11.3.2 引用

38、计数引用计数 引用计数方法的问题引用计数方法的问题 碎片和引用局部性问题仍然存在碎片和引用局部性问题仍然存在 并非总有效:对于循环的数据结构会失效,因为并非总有效:对于循环的数据结构会失效,因为 这些记录的引用计数也永远不可能减到零这些记录的引用计数也永远不可能减到零 代价高,因为每当执行指针存储的时候,都需要代价高,因为每当执行指针存储的时候,都需要 执行额外的指令来调整一些引用计数执行额外的指令来调整一些引用计数 引用计数收集已经被跟踪型收集代替引用计数收集已经被跟踪型收集代替,标记和清标记和清 扫收集方法就是一种跟踪型收集方法扫收集方法就是一种跟踪型收集方法 11.3 无用单元收集无用单

39、元收集 11.3.3 拷贝收集拷贝收集 方法概述方法概述 这个算法和标记和清除算法一样,也遍历可达记这个算法和标记和清除算法一样,也遍历可达记 录所组成的有向图,只不过它在遍历的同时进行录所组成的有向图,只不过它在遍历的同时进行 清扫,并且这种清扫主要是拷贝活跃记录清扫,并且这种清扫主要是拷贝活跃记录 这种方法将堆空间分成大小相等的两块,每块都这种方法将堆空间分成大小相等的两块,每块都 是连续的区域是连续的区域 11.3 无用单元收集无用单元收集 to_spacefrom_spacefrom_space roots free limit (a) 收集前收集前 (b)收集后收集后 rootsto

40、_space free limit 11.3 无用单元收集无用单元收集 11.3.3 拷贝收集拷贝收集 优缺点优缺点 从理论上来说,只要有足够的内存,可得到很高从理论上来说,只要有足够的内存,可得到很高 的效率的效率 可以将活跃数据紧缩在一起,碎片情况消失,引可以将活跃数据紧缩在一起,碎片情况消失,引 用局部性得到改善用局部性得到改善 需要的空间是实际需要空间的两倍需要的空间是实际需要空间的两倍 拷贝大记录的代价太大拷贝大记录的代价太大 11.3 无用单元收集无用单元收集 11.3.4 分代收集分代收集 原理原理 很多程序在运行过程中,新建记录很可能很快死很多程序在运行过程中,新建记录很可能很

41、快死 去,不会出现对它的拷贝;反过来,一个记录在去,不会出现对它的拷贝;反过来,一个记录在 几次收集后还可到达的话,那么很可能还会活跃几次收集后还可到达的话,那么很可能还会活跃 到许多次收集后到许多次收集后 收集器应该将精力集中到收集器应该将精力集中到“年轻年轻”的数据上,因的数据上,因 为这里有相对高的无用单元比率为这里有相对高的无用单元比率 11.3 无用单元收集无用单元收集 11.3.4 分代收集分代收集 基本做法基本做法 堆被分成代,最年轻的记录在堆被分成代,最年轻的记录在g0代,代,gi (i 0)代代 中的记录比中的记录比gi 1代中的任何记录都要老。越年轻代中的任何记录都要老。越

42、年轻 的代越被频繁地收集的代越被频繁地收集 对对g0代进行收集时,这时的根集不仅仅是程序变代进行收集时,这时的根集不仅仅是程序变 量,它还包括量,它还包括g1, g2,中指向中指向g0的指针。幸好,的指针。幸好, 老记录指向年轻得多的记录的情况极少出现,通老记录指向年轻得多的记录的情况极少出现,通 常是较新的记录指向老记录常是较新的记录指向老记录 为了避免确定为了避免确定g0的根集时在的根集时在g1, g2,中查找,需中查找,需 要编译器提供一些支持,方法有多种要编译器提供一些支持,方法有多种 11.3 无用单元收集无用单元收集 11.3.5 渐增式收集渐增式收集 缘由缘由 虽然收集时间的总和

43、只占整个程序运行时间很小虽然收集时间的总和只占整个程序运行时间很小 的百分比,但是收集器仍然有可能偶尔将运行程的百分比,但是收集器仍然有可能偶尔将运行程 序中断相对长的时间,对于交互式程序和实时程序中断相对长的时间,对于交互式程序和实时程 序来说,这一点是难以接受的序来说,这一点是难以接受的 改进改进 渐增式收集渐增式收集:程序运行和无用单元收集交错进行程序运行和无用单元收集交错进行 困难在于,当收集器在做遍历以得到一个可达记困难在于,当收集器在做遍历以得到一个可达记 录图时,运行程序并没有停止修改可达记录图录图时,运行程序并没有停止修改可达记录图 11.3 无用单元收集无用单元收集 11.3

44、.6 编译器与收集器之间的相互影响编译器与收集器之间的相互影响 对收集器的底层有下面这些基本要求对收集器的底层有下面这些基本要求 能确定堆上分配的记录大小能确定堆上分配的记录大小 能定位包含在堆记录里的指针能定位包含在堆记录里的指针 能定位所有在全局变量中的指针能定位所有在全局变量中的指针 能在程序中任何可进行收集的地方找到所有在活能在程序中任何可进行收集的地方找到所有在活 动记录栈中和寄存器中的指针动记录栈中和寄存器中的指针 能找到所有由指针运算所产生的值指向的记录能找到所有由指针运算所产生的值指向的记录 能在记录被移动时,更新所有涉及到的指针值能在记录被移动时,更新所有涉及到的指针值 很多

45、所需信息只有在编译时能够获得很多所需信息只有在编译时能够获得 例例 题题 1 如果如果cfile是一个是一个c语言源程序(注意,该文件名语言源程序(注意,该文件名 没有后缀),在没有后缀),在x86/linux机器上,命令机器上,命令 cc cfile 的结果是错误信息的结果是错误信息 /usr/bin/ld: cfile: file format not recognized: treating as linker script /usr/bin/ld: cfile: 1: parse error collect2: ld returned 1 exit status 请解释为什么会是这样的

46、错误信息请解释为什么会是这样的错误信息 例例 题题 2 下面是下面是c语言的一个程序:语言的一个程序: long gcd(p,q) long p,q; if (p%q = 0) return q; else return gcd(q, p%q); main() printf(n%ldn,gcdx(4,12); 在在x86/linux机器上,用机器上,用gcc命令得到的编译结果如下命令得到的编译结果如下 in function main:undefined reference to gcdx ld returned 1 exit status. 请问,这个请问,这个gcdx没有定义,是在编译时发

47、现的,还是没有定义,是在编译时发现的,还是 在连接时发现的?试说明理由在连接时发现的?试说明理由 例例 题题 3 一些一些c程序设计的教材上指出程序设计的教材上指出“在需要使用标准在需要使用标准i/o库库 中的函数时,应在程序前使用中的函数时,应在程序前使用 #include 预编译命令,但在用预编译命令,但在用printf和和scanf函数时,则可以不函数时,则可以不 要。要。” 但事实上并非仅限于这两个函数。例如下面的但事实上并非仅限于这两个函数。例如下面的 c 程序编译后运行时输出字符程序编译后运行时输出字符a并换行,它并没有预编并换行,它并没有预编 译命令译命令#include 。试解释为什么试解释为什么 main() putchar(a);putchar(n); 例例 题题 4 c的一个源文件可以包含若干个函数,该源文的一个源文件可以包含若干个函数,该源文 件经编译可以生成一个目标文件;若干个目标件经编译可以生成一个目标文件;若干个目标 文件可以构成一个函数库。如果一个用户程序文件可以构成一个函数库。如果一个用户程序 引用库中的某个函数,那么,在连接时的做法引用库中的某个函数,那么,在连接时的做

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论