资源预览内容
第1页 / 共65页
第2页 / 共65页
第3页 / 共65页
第4页 / 共65页
第5页 / 共65页
第6页 / 共65页
第7页 / 共65页
第8页 / 共65页
第9页 / 共65页
第10页 / 共65页
亲,该文档总共65页,到这儿已超出免费预览范围,如果喜欢就下载吧!
资源描述
第十一章第十一章 编译系统和运行系统编译系统和运行系统 本章内容本章内容C语言编译系统语言编译系统预处理器、汇编器预处理器、汇编器、连接器连接器目标文件的格式、静态库、动态连接目标文件的格式、静态库、动态连接Java运行系统运行系统无用单元收集(垃圾收集)无用单元收集(垃圾收集)掌握从源程序到可执行目标程序的实际处理掌握从源程序到可执行目标程序的实际处理过程过程对实际参与软件开发是直接有用的对实际参与软件开发是直接有用的11.1 C语言语言编译系统编译系统预处理器预处理器源程序源程序修改后的源程序修改后的源程序可重定位的目标程序可重定位的目标程序可重定位的可重定位的目标文件目标文件库库编译器编译器汇编器汇编器汇编程序汇编程序连接器连接器可执行的目标程序可执行的目标程序 C源程序可以分成源程序可以分成若干个模块若干个模块 分别进行预处理、分别进行预处理、编译和汇编、形成编译和汇编、形成可重定位的目标文可重定位的目标文件件 目标文件和必要目标文件和必要的库文件连接成一的库文件连接成一个可执行的目标文个可执行的目标文件件 gccgcc和和cccc是编译驱是编译驱动程序的名字动程序的名字 11.1 C语言语言编译系统编译系统main.c(1) #if 1(2) int buf2; (3) #else(4) int buf2 = 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;(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) 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,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语言语言编译系统编译系统一段汇编代码一段汇编代码.L2:cmpl $0,-4(%ebp)jne .L6jmp .L11.L11:cmpl $0,-8(%ebp)jne .L6jmp .L12.L12:jmp .L5.p2align 4,7.L6:11.1 C语言语言编译系统编译系统11.1.3 连接器连接器目标模块或目标文件的形式目标模块或目标文件的形式可重定位的目标文件可重定位的目标文件可执行的目标文件可执行的目标文件共享目标文件共享目标文件 一种特殊的可重定位目标文件一种特殊的可重定位目标文件 在装入程序或运行程序时,动态地装入到内存并在装入程序或运行程序时,动态地装入到内存并连接连接11.1 C语言语言编译系统编译系统连接连接是一个收集、组织程序所需的不同代码是一个收集、组织程序所需的不同代码和数据的过程,以便程序能被装入内存并被和数据的过程,以便程序能被装入内存并被执行执行连接的时机连接的时机编译时编译时装入时装入时运行时运行时静态连接器静态连接器动态连接器动态连接器11.1 C语言语言编译系统编译系统一个重定位模块一个重定位模块M可能可能定义和引用的符号定义和引用的符号全局符号全局符号 指那些在模块指那些在模块M中定义,可以被其它模中定义,可以被其它模块引用的符号块引用的符号局部符号局部符号 指那些在模块指那些在模块M中定义,且只能在本模中定义,且只能在本模块中引用的符号块中引用的符号外部符号外部符号 指那些由模块指那些由模块M引用并由其它模块定义引用并由其它模块定义符号符号符号解析符号解析识别各个目标模块中定义和引用的符号,为每一识别各个目标模块中定义和引用的符号,为每一个符号引用确定它所关联的一个同名符号的定义个符号引用确定它所关联的一个同名符号的定义重定位重定位11.1 C语言语言编译系统编译系统11.1.4 目标文件的格式目标文件的格式目标文件格式随系统不同而不同目标文件格式随系统不同而不同介绍介绍Unix的的ELF(Executable and Linkable Format)格式格式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.1 C语言语言编译系统编译系统节头表节头表描述目标文件中各节的描述目标文件中各节的位置和大小位置和大小处于目标文件的末尾处于目标文件的末尾ELF头头.text.rodata.data.bss.symtab.rel.text.rel.data.debug.line.strtab节节头头表表0描述目标文描述目标文件的节件的节节节11.1 C语言语言编译系统编译系统.text节节 被编译程序的机器代码被编译程序的机器代码.rodata节节 诸如诸如printf语句中的格语句中的格式串和式串和switch语句的跳语句的跳转表等只读数据转表等只读数据.data节节 已初始化的全局变量已初始化的全局变量 ELF头头.text.rodata.data.bss.symtab.rel.text.rel.data.debug.line.strtab节节头头表表0描述目标文描述目标文件的节件的节节节11.1 C语言语言编译系统编译系统.bss节(节(.comm 节)节) 未初始化的全局变量未初始化的全局变量 在目标文件中不占实际在目标文件中不占实际的空间的空间.symtab节节记录在该模块中定义和记录在该模块中定义和引用的函数和全局变量引用的函数和全局变量的信息的符号表的信息的符号表ELF头头.text.rodata.data.bss.symtab.rel.text.rel.data.debug.line.strtab节节头头表表0描述目标文描述目标文件的节件的节节节11.1 C语言语言编译系统编译系统.symtab节节TypeFUNCOBJECTBindGLOBALLOCALEXTERNELF头头.text.rodata.data.bss.symtab.rel.text.rel.data.debug.line.strtab节节头头表表0描述目标文描述目标文件的节件的节节节11.1 C语言语言编译系统编译系统.symtab节节NameValue偏移偏移地址,或地址,或绝对绝对地址地址Size字节数字节数ELF头头.text.rodata.data.bss.symtab.rel.text.rel.data.debug.line.strtab节节头头表表0描述目标文描述目标文件的节件的节节节11.1 C语言语言编译系统编译系统.rel.text节节 .text节节中需要修改的中需要修改的单元的位置列表单元的位置列表.rel.data节节用于被本模块引用或定用于被本模块引用或定义的全局变量的重定位义的全局变量的重定位信息信息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.rel.text.rel.data.debug.line.strtab节节头头表表0描述目标文描述目标文件的节件的节节节11.1 C语言语言编译系统编译系统11.1.5 符号解析符号解析将每个符号引用正确地与某可重定位模块的将每个符号引用正确地与某可重定位模块的符号表中的一个符号定义相关联,从而确定符号表中的一个符号定义相关联,从而确定各个符号引用的位置各个符号引用的位置在所有输入模块中都找不到被引用符号的定在所有输入模块中都找不到被引用符号的定义,则打印错误消息并结束连接义,则打印错误消息并结束连接需要定义解析规则需要定义解析规则11.1 C语言语言编译系统编译系统解析规则解析规则函数和已初始化的全局变量称为强符号;未函数和已初始化的全局变量称为强符号;未初始化的全局变量称为弱符号初始化的全局变量称为弱符号不允许有多重的强符号定义不允许有多重的强符号定义出现一个强符号定义和多个弱符号定义时,出现一个强符号定义和多个弱符号定义时,选择强符号的定义选择强符号的定义出现多个弱符号定义时,选择任意一个弱符出现多个弱符号定义时,选择任意一个弱符号的定义号的定义 11.1 C语言语言编译系统编译系统11.1.6 静态库静态库将相关的可重定位目标模块打包成一个文件将相关的可重定位目标模块打包成一个文件, ,作为连接器的输入作为连接器的输入连接器仅复制库中被应用程序引用的模块连接器仅复制库中被应用程序引用的模块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.omylib.aswap.olibc.a 静态库静态库swap1完全连接的可执行文件完全连接的可执行文件和静态库连接和静态库连接 11.1 C语言语言编译系统编译系统11.1.7 可执行目标文件及装入可执行目标文件及装入可执行目标文件可执行目标文件与可重定位目标文件格式类与可重定位目标文件格式类似似可执行目标文件的装入由加载器完成可执行目标文件的装入由加载器完成11.1 C语言语言编译系统编译系统读读/ /写内存段写内存段ELF头头段头表段头表.init.text.rodata.data.bss.symtab.debug.line.strtab节头表节头表只读内存段只读内存段符符号号表表和和调调试试信信息,不装入内存息,不装入内存描述目标文件描述目标文件的节的节将下面的节映射到将下面的节映射到运行时的内存段运行时的内存段典型的典型的ELF可执行目标文件可执行目标文件11.1 C语言语言编译系统编译系统Linux运行时的内存映像运行时的内存映像内核内核用户栈用户栈(运行时创建)(运行时创建)共享库的共享库的内存区域内存区域运行时的堆运行时的堆(运行时用(运行时用malloc创建)创建)读读/写段写段(.data, .bss)只读段只读段(.init, .text, .rodata)未使用未使用0xc00000000x400000000x08048000对用户代码对用户代码不可见不可见 %esp(栈指针)(栈指针)brk从可执行文件从可执行文件装入装入011.1 C语言语言编译系统编译系统这里描述的装入过程从概念上来说是正确的这里描述的装入过程从概念上来说是正确的若需要了解装入过程真正是怎样工作的,必若需要了解装入过程真正是怎样工作的,必须在理解了进程、虚拟内存和内存分页等概须在理解了进程、虚拟内存和内存分页等概念以后念以后11.1 C语言语言编译系统编译系统11.1.8 动态连接动态连接静态库静态库 周期性地被维护和更新周期性地被维护和更新 内存可能有多份内存可能有多份printf和和scanf的代码的代码共享库共享库 在运行时可以装到任意的内存位置,被内存中在运行时可以装到任意的内存位置,被内存中的进程共享的进程共享11.1 C语言语言编译系统编译系统共享库以两种不同的方式被共享共享库以两种不同的方式被共享共享库的代码和数据被所有引用该库的可执共享库的代码和数据被所有引用该库的可执行目标文件所共享行目标文件所共享共享库的共享库的.text节在内存中的一个副本可以被节在内存中的一个副本可以被正在运行的不同进程共享正在运行的不同进程共享11.1 C语言语言编译系统编译系统可重定位文件可重定位文件翻译器翻译器(cpp,cc1,as)main.c源文件源文件连接器(连接器(ld)main.olibc.somylib.so重重定定位位和和符符号表信息号表信息部分连接的可执部分连接的可执行目标文件行目标文件swap2加载器(加载器(execve)libc.somylib.so动态连接器(动态连接器(ld-linux.so)代码和代码和数据数据此时,动态连接器是内存中此时,动态连接器是内存中已完全连接的可执行代码已完全连接的可执行代码11.1 C语言语言编译系统编译系统加载器通常装入和运行动态连接器加载器通常装入和运行动态连接器动态连接器接着完成连接任务动态连接器接着完成连接任务把把libc.so的文本和数据装入内存并进行重定位的文本和数据装入内存并进行重定位把把mylib.so的文本和数据装入内存并进行重定的文本和数据装入内存并进行重定位位重定位重定位swap2中任何对中任何对libc.so或或mylib.so定义定义的符号的引用的符号的引用将控制传递给应用程序将控制传递给应用程序11.1 C语言语言编译系统编译系统11.1.9 处理目标文件的一些工具处理目标文件的一些工具ar创建静态库,插入、删除、罗列和提取成创建静态库,插入、删除、罗列和提取成strings 列出包含在目标文件中的所有可打印串列出包含在目标文件中的所有可打印串strip 从一个目标文件中删除符号表信息从一个目标文件中删除符号表信息nm 列出一个目标文件的符号表中定义的符号列出一个目标文件的符号表中定义的符号size 列出目标文件中各段的名字和大小列出目标文件中各段的名字和大小readelf 显示目标文件的完整结构,包括编码在显示目标文件的完整结构,包括编码在ELF头头中的所有信息。它包括了中的所有信息。它包括了size和和nm的功能的功能objdump可以显示目标文件中的所有信息。其最有可以显示目标文件中的所有信息。其最有用的功能是反汇编用的功能是反汇编.text节中的二进制指令节中的二进制指令ldd列出可执行目标文件在运行时需要的共享库列出可执行目标文件在运行时需要的共享库 11.2 Java语言的运行系统语言的运行系统Java语言语言简单性、分布性、安全性、可移植性等简单性、分布性、安全性、可移植性等我们关心的是:平台无关性我们关心的是:平台无关性Java虚虚拟拟机机技技术术是是实实现现Java平平台台无无关关性性特特点点的关键的关键Java运行系统就是运行系统就是Java虚拟机的一个实现虚拟机的一个实现11.2 Java语言的运行系统语言的运行系统11.2.1 Java虚拟机语言简介虚拟机语言简介Java程序首先由程序首先由Java编译器把它编译成字节码编译器把它编译成字节码, ,也就是也就是JVML程序程序常常量量池池: : 各各种种字字符符串串常常量量, ,类类似似于于传传统统程程序序设设计语言中的符号表计语言中的符号表类成员信息类成员信息: : 域信息表和方法信息表域信息表和方法信息表JVML指令序列指令序列11.2 Java语言的运行系统语言的运行系统Java源程序中的方法源程序中的方法int calculate (int i)int j =2;return (i+j) (j-1); 对应的字节码程序:对应的字节码程序:int calculate (int i)iconst_2istore_2iload_1iload_2iaddiload_2iconst_1isubimulireturn11.2 Java语言的运行系统语言的运行系统11.2.2 Java虚拟机虚拟机Java虚拟机一般由以下几个部分构成:虚拟机一般由以下几个部分构成:类加载器(字节码验证器)类加载器(字节码验证器)解释器或解释器或/和编译器和编译器包包括括无无用用单单元元收收集集器器和和线线程程控控制制模模块块在在内内的的运行支持系统,运行支持系统,另另外外还还有有一一些些标标准准类类和和应应用用接接口口的的class文文件件库库Java源源程序程序Java字字节码节码Java编编译器译器Java字字节码节码在网络在网络上移动上移动Java虚拟机虚拟机类加载器(字节类加载器(字节码验证)码验证)Java类库类库字节码解释器字节码解释器即时编译器即时编译器无用单元收集器无用单元收集器线程线程/ /同步机制同步机制运行时支持运行时支持LinuxWin32/NT硬件设备硬件设备Solaris11.2 Java语言的运行系统语言的运行系统C语言语言数据栈数据栈用用来来存存放放生生存存期期和和过过程程一一次次活活动动的的生生存期一致的局部变量存期一致的局部变量数据堆数据堆用用来来存存放放生生存存期期和和过过程程一一次次活活动动的的生生存期不一定一致的动态变量存期不一定一致的动态变量 程序员通过程序员通过malloc和和free函数参与堆的管理函数参与堆的管理 不安全的一个根源(悬空指针、内存泄漏)不安全的一个根源(悬空指针、内存泄漏)Java语言语言数据栈数据栈除对象和数组外,都分配在栈上除对象和数组外,都分配在栈上数据堆数据堆对象和数组分配在堆上对象和数组分配在堆上 出于安全的要求,程序员不参与堆管理出于安全的要求,程序员不参与堆管理11.2 Java语言的运行系统语言的运行系统无用单元收集(俗称垃圾收集)无用单元收集(俗称垃圾收集)无用单元(理论上)无用单元(理论上)那些在继续运行过程中不会再使用的数据单元那些在继续运行过程中不会再使用的数据单元收集器采用稳妥策略收集器采用稳妥策略实实际际上上并并非非总总能能判判断断出出一一个个数数据据记记录录的的值值以以后后是否还需要是否还需要通通过过根根集集(roots set,在在栈栈上上)以以及及从从根根集集开开始始的可达性来定义变量的活跃性的可达性来定义变量的活跃性无用单元(实现上)无用单元(实现上)通通常常指指那那些些不不可可能能从从程程序序变变量量经经指指针针链链到到达达的的堆分配记录堆分配记录11.2 Java语言的运行系统语言的运行系统11.2.3 即时编译器即时编译器当当一一个个类类的的某某个个方方法法第第一一次次被被调调用用时时,虚虚拟拟机才激活即时编译器将它编译成机器代码机才激活即时编译器将它编译成机器代码生生成成的的代代码码的的执执行行速速度度可可以以达达到到解解释释执执行行的的10倍倍但但是是执执行行过过程程不不得得不不等等待待编编译译的的结结束束,因因此此使得执行时间变长使得执行时间变长很很多多虚虚拟拟机机都都会会使使用用快快速速解解释释器器和和优优化化编编译译器器的的组组合合或或者者是是简简单单编编译译器器和和复复杂杂编编译译器器的的组合组合11.2 Java语言的运行系统语言的运行系统即时编译即时编译对象引用对象引用方法表指针方法表指针实例数据实例数据方法表方法表类指针类指针方法方法0代码指针代码指针方法方法1代码指针代码指针compile-me代码段代码段编编译译得得到到的的机机器器代码代码( (本地代码本地代码) )即时编译器即时编译器 11.2 Java语言的运行系统语言的运行系统重编译机制重编译机制字节码字节码未优化未优化机器代码机器代码优化机优化机器代码器代码快快速速代代码码生生成的编译器成的编译器优化编译器优化编译器计数器计数器统计数据统计数据11.3 无用单元收集无用单元收集 无无用用单单元元收收集集器器需需要要根根据据数数据据的的活活跃跃性性来来判判断断哪哪些是无用单元些是无用单元活活跃跃性性分分析析采采用用稳稳妥妥策策略略,是是通通过过根根集集以以及及从从根根集开始的可达性来定义活跃性集开始的可达性来定义活跃性因因此此从从实实现现的的角角度度,无无用用单单元元是是那那些些不不可可能能从从程程序变量经指针链到达的堆分配记录序变量经指针链到达的堆分配记录 无无用用单单元元收收集集可可能能需需要要来来自自编编译译器器、操操作作系系统统和和硬件方面的支持硬件方面的支持本本节节简简要要介介绍绍一一些些主主要要的的无无用用单单元元收收集集方方法法,并并且描述编译器和收集器之间的一些相互影响且描述编译器和收集器之间的一些相互影响11.3 无用单元收集无用单元收集 11.3.1 标记和清扫标记和清扫方方法法概概述述:首首先先标标记记堆堆上上所所有有可可达达记记录录,然然后回收未被标记的记录后回收未被标记的记录 根根集集包包含含了了全全局局变变量量、活活动动记记录录栈栈中中的的局局部部变变量量和被活跃着的过程使用的寄存器和被活跃着的过程使用的寄存器堆堆上上活活跃跃记记录录的的集集合合是是从从根根集集开开始始的的任任何何一一条条指指针路径上的记录的集合针路径上的记录的集合任何图遍历算法,都可用于标记所有的可达记录任何图遍历算法,都可用于标记所有的可达记录清清扫扫从从堆堆的的首首地地址址开开始始, 逐逐个个记记录录地地考考察察整整个个堆堆, 寻找未被标记的记录寻找未被标记的记录, 把它们链成一个空闲链表把它们链成一个空闲链表 11.3 无用单元收集无用单元收集 11.3.1 标记和清扫标记和清扫传统的标记和清扫方法的问题传统的标记和清扫方法的问题 碎碎片片问问题题:当当要要分分配配一一个个n字字节节大大小小的的记记录录时时,发发现现有有很很多多小小于于n字字节节的的空空闲闲块块存存在在,但但就就是是没没有有合合适的空闲块可分配给这个记录适的空闲块可分配给这个记录引引用用局局部部性性问问题题:使使用用块块和和空空闲闲块块相相互互交交织织,使使得得当当前前要要使使用用的的各各个个活活跃跃记记录录被被分分散散到到很很多多的的虚虚拟拟内内存存页页中中,这这些些页页在在内内存存中中可可能能被被频频繁繁地地换换进进换出换出 11.3 无用单元收集无用单元收集 11.3.2 引用计数引用计数方方法法概概述述:通通过过记记住住有有多多少少指指针针指指向向每每个个记记录来直接完成标记,引用计数存在各记录中录来直接完成标记,引用计数存在各记录中编编译译器器需需要要在在每每个个出出现现指指针针存存储储的的地地方方生生成成额额外外的指令,以调整一些引用计数器的值的指令,以调整一些引用计数器的值当当一一个个记记录录的的引引用用计计数数值值为为0的的时时候候,就就可可以以把把该该记录加入空闲链表记录加入空闲链表被被回回收收记记录录本本身身的的指指针针域域都都要要一一一一检检查查,它它们们所所指向的记录的引用计数值也都要减指向的记录的引用计数值也都要减1 11.3 无用单元收集无用单元收集 11.3.2 引用计数引用计数引用计数方法的问题引用计数方法的问题碎片和引用局部性问题仍然存在碎片和引用局部性问题仍然存在并并非非总总有有效效:对对于于循循环环的的数数据据结结构构会会失失效效,因因为为这些记录的引用计数也永远不可能减到零这些记录的引用计数也永远不可能减到零代代价价高高,因因为为每每当当执执行行指指针针存存储储的的时时候候,都都需需要要执行额外的指令来调整一些引用计数执行额外的指令来调整一些引用计数引引用用计计数数收收集集已已经经被被跟跟踪踪型型收收集集代代替替,标标记记和和清清扫收集方法就是一种跟踪型收集方法扫收集方法就是一种跟踪型收集方法11.3 无用单元收集无用单元收集 11.3.3 拷贝收集拷贝收集方法概述方法概述这这个个算算法法和和标标记记和和清清除除算算法法一一样样,也也遍遍历历可可达达记记录录所所组组成成的的有有向向图图,只只不不过过它它在在遍遍历历的的同同时时进进行行清扫,并且这种清扫主要是拷贝活跃记录清扫,并且这种清扫主要是拷贝活跃记录这这种种方方法法将将堆堆空空间间分分成成大大小小相相等等的的两两块块,每每块块都都是连续的区域是连续的区域11.3 无用单元收集无用单元收集 to_spacefrom_spacefrom_space rootsfreelimit(a) 收集前收集前(b)收集后收集后rootsto_spacefreelimit11.3 无用单元收集无用单元收集 11.3.3 拷贝收集拷贝收集优缺点优缺点从从理理论论上上来来说说,只只要要有有足足够够的的内内存存,可可得得到到很很高高的效率的效率可可以以将将活活跃跃数数据据紧紧缩缩在在一一起起,碎碎片片情情况况消消失失,引引用局部性得到改善用局部性得到改善需要的空间是实际需要空间的两倍需要的空间是实际需要空间的两倍拷贝大记录的代价太大拷贝大记录的代价太大11.3 无用单元收集无用单元收集 11.3.4 分代收集分代收集原理原理很很多多程程序序在在运运行行过过程程中中,新新建建记记录录很很可可能能很很快快死死去去,不不会会出出现现对对它它的的拷拷贝贝;反反过过来来,一一个个记记录录在在几几次次收收集集后后还还可可到到达达的的话话,那那么么很很可可能能还还会会活活跃跃到许多次收集后到许多次收集后收收集集器器应应该该将将精精力力集集中中到到“年年轻轻”的的数数据据上上,因因为这里有相对高的无用单元比率为这里有相对高的无用单元比率 11.3 无用单元收集无用单元收集 11.3.4 分代收集分代收集基本做法基本做法堆堆被被分分成成代代,最最年年轻轻的的记记录录在在G0代代,Gi (i 0)代代中中的的记记录录比比Gi 1代代中中的的任任何何记记录录都都要要老老。越越年年轻轻的代越被频繁地收集的代越被频繁地收集对对G0代代进进行行收收集集时时,这这时时的的根根集集不不仅仅仅仅是是程程序序变变量量,它它还还包包括括G1, G2,中中指指向向G0的的指指针针。幸幸好好,老老记记录录指指向向年年轻轻得得多多的的记记录录的的情情况况极极少少出出现现,通通常是较新的记录指向老记录常是较新的记录指向老记录为为了了避避免免确确定定G0的的根根集集时时在在G1, G2,中中查查找找,需需要编译器提供一些支持,方法有多种要编译器提供一些支持,方法有多种11.3 无用单元收集无用单元收集 11.3.5 渐增式收集渐增式收集缘由缘由虽虽然然收收集集时时间间的的总总和和只只占占整整个个程程序序运运行行时时间间很很小小的的百百分分比比,但但是是收收集集器器仍仍然然有有可可能能偶偶尔尔将将运运行行程程序序中中断断相相对对长长的的时时间间,对对于于交交互互式式程程序序和和实实时时程程序来说,这一点是难以接受的序来说,这一点是难以接受的改进改进渐增式收集渐增式收集:程序运行和无用单元收集交错进行程序运行和无用单元收集交错进行困困难难在在于于,当当收收集集器器在在做做遍遍历历以以得得到到一一个个可可达达记记录图时,运行程序并没有停止修改可达记录图录图时,运行程序并没有停止修改可达记录图 11.3 无用单元收集无用单元收集 11.3.6 编译器与收集器之间的相互影响编译器与收集器之间的相互影响对收集器的底层有下面这些基本要求对收集器的底层有下面这些基本要求 能确定堆上分配的记录大小能确定堆上分配的记录大小能定位包含在堆记录里的指针能定位包含在堆记录里的指针能定位所有在全局变量中的指针能定位所有在全局变量中的指针能在程序中任何可进行收集的地方找到所有在活能在程序中任何可进行收集的地方找到所有在活动记录栈中和寄存器中的指针动记录栈中和寄存器中的指针能找到所有由指针运算所产生的值指向的记录能找到所有由指针运算所产生的值指向的记录能在记录被移动时,更新所有涉及到的指针值能在记录被移动时,更新所有涉及到的指针值很多所需信息只有在编译时能够获得很多所需信息只有在编译时能够获得例例 题题 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 errorcollect2: ld returned 1 exit status请解释为什么会是这样的错误信息请解释为什么会是这样的错误信息例例 题题 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 gcdxld returned 1 exit status.请问,这个请问,这个gcdx没有定义,是在编译时发现的,还是没有定义,是在编译时发现的,还是在连接时发现的?试说明理由在连接时发现的?试说明理由例例 题题 3一些一些C程序设计的教材上指出程序设计的教材上指出“在需要使用标准在需要使用标准I/O库库中的函数时,应在程序前使用中的函数时,应在程序前使用#include 预编译命令,但在用预编译命令,但在用printf和和scanf函数时,则可以不函数时,则可以不要要。” 但但事事实实上上并并非非仅仅限限于于这这两两个个函函数数。例例如如下下面面的的C程序编译后运行时输出字符程序编译后运行时输出字符A并换行,它并没有预编并换行,它并没有预编译命令译命令#include 。试解释为什么试解释为什么main()putchar(A);putchar(n);例例 题题 4C的一个源文件可以包含若干个函数,该源文的一个源文件可以包含若干个函数,该源文件经编译可以生成一个目标文件;若干个目标件经编译可以生成一个目标文件;若干个目标文件可以构成一个函数库。如果一个用户程序文件可以构成一个函数库。如果一个用户程序引用库中的某个函数,那么,在连接时的做法引用库中的某个函数,那么,在连接时的做法是下面三种情况的哪一种,说明你的理由是下面三种情况的哪一种,说明你的理由将该库函数的目标代码连到用户程序将该库函数的目标代码连到用户程序将将该该库库函函数数的的目目标标代代码码所所在在的的目目标标文文件件连连到到用户程序用户程序将该函数库全部连到用户程序将该函数库全部连到用户程序例例 题题 5cc是是UNIX系统上系统上C语言编译命令,语言编译命令, l是连接库是连接库函数的选择项。某程序员自己编写了两个函数函数的选择项。某程序员自己编写了两个函数库库libuser1.a和和libuser2.a (库库名名必必须须以以lib为为前前缀缀),当用命令当用命令cc test.c luser1.a luser2.a编译时,报告有未定义的符号,而改用命令编译时,报告有未定义的符号,而改用命令cc test.c luser2.a luser1.a时,能得到可执行程序。试分析原因时,能得到可执行程序。试分析原因(备注:库名中的备注:库名中的lib在命令中省略。该命令和命在命令中省略。该命令和命令令cc test.c libuser1.a libuser2.a的效果一致的效果一致)例例 题题 5cc test.c luser1.a luser2.a解答解答 test.c libuser1.a libuser2.a引用引用a定义定义b定义定义a引用引用b例例 题题 6cc是是UNIX系统上系统上C语言编译命令,语言编译命令, l是连接库是连接库函数的选择项。两个程序员分别编写了函数库函数的选择项。两个程序员分别编写了函数库libuser1.a和和libuser2.a。当用命令。当用命令cc test.c luser1.a luser2.a编译时,报告有重复定义的符号。而改用命令编译时,报告有重复定义的符号。而改用命令cc test.c luser2.a luser1.a时,能得到可执行程序。试分析原因时,能得到可执行程序。试分析原因例例 题题 6cc test.c luser1.a luser2.a一种解答一种解答 test.c libuser1.a libuser2.a引用引用a定义定义a定义定义b引用引用b定义定义a这种情况在若干人一起开发软件时有可能发生这种情况在若干人一起开发软件时有可能发生 例例 题题 7两个两个C语言文件语言文件link1.c和和link2.c的内容分别如下的内容分别如下int buf1 =100;和和extern int buf;main()printf(“%dn”, buf);在在X86/Linux经命令经命令cc link1.c link2.c编译后,运编译后,运行时产生如下的出错信息行时产生如下的出错信息Segmentation fault (core dumped)请说明原因请说明原因例例 题题 7int buf1 =100;和和extern int buf;main() printf(“%dn”, buf); 目标文件连接时并不检查名字的类型。虽然两个文目标文件连接时并不检查名字的类型。虽然两个文件对件对buf的类型持不同观点,但仍能连接成目标程序的类型持不同观点,但仍能连接成目标程序目标文件连接时,是让不同文件中同一名字的地址目标文件连接时,是让不同文件中同一名字的地址相同。因此,运行时,在相同。因此,运行时,在link2.c中,由于中,由于buf的内容的内容是是100,取,取 buf的值就是取地址的值就是取地址100的内容。该地址的内容。该地址不在用户数据区内,因此报错不在用户数据区内,因此报错 若把这些代码放在一个文件中,编译时报错若把这些代码放在一个文件中,编译时报错习习 题题
收藏 下载该资源
网站客服QQ:2055934822
金锄头文库版权所有
经营许可证:蜀ICP备13022795号 | 川公网安备 51140202000112号