少于 1 分钟阅读 次阅读

一、 什么是编译(Compilation)?

编译,是将“人类可读的源代码”翻译成“机器可读的目标文件”的过程。

  • 工作单元: 编译器(如 GCC/Clang)是以单个源文件(.c.cpp)为基本单位工作的。这个单位在术语中被称为“翻译单元”(Translation Unit)。
  • 它的任务:
  1. 读取一个 .c 文件以及它 #include 的所有头文件。
  2. 进行词法分析、语法分析,检查代码有没有写错。
  3. 将其翻译成针对特定 CPU 架构的机器码,生成一个目标文件(通常后缀是 .o.obj)。
  • 遇到未知事物的处理方式: 假设 main.c 里调用了一个函数 print_hello(),但这个函数定义在 utils.c 里。编译器在编译 main.c 时,找不到这个函数的具体实现代码。此时编译器不会报错,而是会在生成的 main.o 文件里留下一个“记号”(未决符号):“这里需要调用 print_hello,但我不知道它在内存的哪里,请后面的兄弟帮忙填上。”

形象比喻: 就像是一个翻译团队在翻译一本多章节的外文书。每个人(编译器进程)单独领走一章(.c 文件)去翻译,互不干扰。遇到跨章节的引用(比如“参见第五章”),翻译员只照字面翻译,不管第五章到底在哪一页。


二、 什么是链接(Linking)?

链接,是将所有零散的“目标文件(.o)”以及外部库(.a.so.dll)缝合拼装成一个完整“可执行文件(.exe.elf)”的过程。

  • 工作单元: 链接器(如 ld、lld、mold)必须具有全局视角,它面对的是项目中成百上千个 .o 文件。
  • 它的任务:
  1. 符号决议(Symbol Resolution): 链接器会扫描所有的 .o 文件,把编译器留下的那些“记号”全部填上真实的内存地址。它会把 main.o 中对 print_hello() 的调用,精准地指向 utils.o 中该函数的实际位置。
  2. 地址空间分配: 把所有文件中的代码段(指令)和数据段(全局变量)合并,并为它们分配最终运行时的虚拟内存地址。
  3. 合并调试信息: 如果是 Debug 模式,它还要把所有分散的调试符号表合并到一起。

形象比喻: 链接器就像是出版社的“总编排”。他要把所有翻译好的章节收上来,排版装订成一册,最重要的是生成“目录”,并把书里所有的“参见第五章”替换成具体的“参见第 142 页”。


三、 为什么它们使用的内存大小完全不一样?

了解了它们的工作原理,内存消耗的差异就不言而喻了。

1. 编译阶段:内存消耗极小且可控

  • 局部性: 一个编译器进程同一时间只看一个文件。无论你的项目有一百个文件还是一万个文件,编译单个文件所需的内存只与该文件(及展开后的头文件)的复杂度有关。
  • 阅后即焚: 一旦 .o 文件生成并写入磁盘,编译器进程就会关闭,这部分内存立刻释放。
  • 并发友好: 如果你开 32 个并发(-j32),系统只是同时运行 32 个消耗少量内存的进程。普通电脑的内存完全扛得住。

2. 链接阶段:内存消耗是灾难级的“内存黑洞”

链接阶段是整个构建过程的瓶颈,尤其在大型 C/C++ 项目(如 Chromium、UE5 游戏引擎、LLVM 编译器本身)中,链接器吃掉 几十 GB 甚至上百 GB 的物理内存 是家常便饭。原因如下:

  • 必须全局加载: 链接器需要进行全局的符号匹配。为了快速查找,它必须将所有.o 文件的符号表和字符串表同时加载到内存中,并构建极其庞大的哈希映射树(Hash Table/Map)。
  • 调试信息(Debug Info)爆炸: 现代 C++ 充满了模板(Templates)。每个包含模板的 .o 文件都会生成大量的调试信息。链接器需要把几十个 G 的冗余调试信息读入内存,进行去重和压缩。
  • LTO(链接时优化,Link-Time Optimization)的代价: 这是现代编译器榨干性能的终极武器。开启 LTO 后,编译器生成的 .o 不再是机器码,而是中间代码(IR)。真正的代码生成和跨文件极致优化被推迟到了链接阶段。这意味着链接器要把整个项目的抽象语法树/中间表示全部载入内存,进行全局的内联、无用代码消除。这极其消耗内存。

总结

  • 编译是“单点作战”,吃的是 CPU 的多核并行计算能力,内存开销小。
  • 链接是“全局汇总”,吃的是 内存容量和磁盘 I/O

如果在大型项目中不对链接进行限制(比如同时启动 5 个链接任务去生成 5 个不同的测试程序),每个链接任务索要 15 GB 内存,瞬间就会把一台 64GB 内存的工作站吃撑,导致系统 OOM(Out of Memory)甚至直接死机。这就是为什么 Ninja 这种底层工具必须提供 pool 机制,强制把链接操作排队单线执行的原因。

标签:

分类:

更新时间:

留下评论