Keyboard shortcuts

Press or to navigate between chapters

Press ? to show this help

Press Esc to hide this help

内存分配与碎片

复习

  • 页面置换:内存装满后应该换出哪一页
  • 工作集与内存抖动:程序频繁缺页时为什么会忙着换页却不再做事
  • 写时复制:先共享页面,真正修改时再复制

TL;DR

  • 内存要不停地被分配和回收,得有专门的分配器打理
  • 内部碎片:分出去的一块里没用到的那部分
  • 外部碎片:零散的空闲块,单个都太小、拼不起来
  • 分配策略(首次匹配、最佳匹配等)各有取舍

正文

  前面的分页、置换,都在讲“页”这一层。可程序要内存时,并不是按页来要的——它要的是“一块能放 100 个整数的地方”。从“页”到“一块大小不定的内存”,中间还需要一个内存分配器

两种分配场合

  内存分配大致分两个层面:

  • 内核的分配:操作系统自己需要内存,比如创建进程、记录结构
  • 运行时的分配:程序运行中申请内存,比如动态创建一个对象、一个变长数组

  不管哪一层,分配器都要面对同一件事:从一片空闲区域里,切出一块给申请者,用完再收回来。

两种碎片

  切来切去、收来收去,就会产生碎片。碎片有两类:

  • 内部碎片:分出去的这一块,比实际需要的稍大,多出来的部分被浪费。分页的“最后一页填不满”就是典型。
  • 外部碎片:空闲的内存被切得七零八落,单个空块都太小,凑不出一块连续的大空位。分段留下的空隙就是典型。
外部碎片示意:
[占用][空][占用][空][空][占用][空]
      ↑太小  ↑太小(合起来也许够,但不连续)

  内部碎片是“给多了浪费”,外部碎片是“零散得用不上”。

分配策略

  从一个空闲块里切一块出来,切哪儿?常见几种挑法:

  • 首次匹配:从头上找,第一个够大的就用
  • 最佳匹配:找最接近所需大小的那一块,最省但查找慢、还容易留下更小的碎块
  • 最差匹配:找最大的那块来切,切完剩下的还够用

  它们各有优劣,没有一种总能赢。实际系统往往还会做内存整理(把散块挪到一起)来对抗外部碎片——但整理本身也有代价。

  到这里,内存这条线就基本讲完了。接下来换个方向:程序总要和设备、和磁盘打交道,那又是怎么组织的?下一章,看操作系统怎样面对形形色色的设备。

思考题

  “内部碎片”和“外部碎片”,哪一种更麻烦?为什么分页选择接受内部碎片,而分段却会留下外部碎片?

小结

知识点

  • 内存分配器负责切分和回收空闲内存
  • 内部碎片:分配块内没用上的部分
  • 外部碎片:零散、拼不起来的小空块
  • 首次/最佳/最差匹配等分配策略各有取舍

参考资料

  1. Wikipedia(zh):内存分配:memory allocation
  2. Wikipedia(zh):碎片化:fragmentation

思考题答案(仅供参考)

  一般来说,外部碎片更麻烦:内部碎片顶多是“一块里浪费一点”,浪费有上限;而外部碎片可能让一大片内存因为“凑不齐连续空间”而根本用不上,且越用越碎。分页之所以接受内部碎片,是因为固定大小的块彻底消灭了外部碎片——最后一点浪费换来“任何空块都能用”,很划算。分段则因为段长不定,天然会产生外部碎片。这也说明:在内部碎片和外部碎片之间,工程师往往宁愿选前者。

协议

  本作品采用知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议进行许可。

封面图

设计师 | 南国微雪