堆管理问题

堆用于运行时动态分配内存(malloc/freenew/delete)。

分配策略

  • 首次适应(First-Fit):选择第一个足够大的空闲块
  • 最佳适应(Best-Fit):选择最接近请求大小的空闲块

内存碎片

  • 内部碎片:分配块内部未使用的空间
  • 外部碎片:空闲块被分割成多个小碎片,无法满足大块请求

手动内存管理的问题

  • 悬空指针:释放后继续使用(use-after-free)
  • 内存泄漏:不再使用的内存未释放

垃圾收集(GC, Garbage Collection)

GC 基本原理

  • 根集(Root Set):所有栈帧和全局变量中的引用
  • 可达性分析:从根集出发,可达对象为”存活”,不可达为”垃圾”
  • Stop the World:GC 执行时暂停所有应用线程

引用计数(Reference Counting)

每个对象维护引用计数器,赋值时更新:

  • 新增引用 → 计数器 +1
  • 删除引用 → 计数器 -1
  • 计数器归零 → 回收

优点:实现简单,实时回收 缺点:无法处理循环引用,性能开销大

标记-清除(Mark-Sweep)

两阶段算法:

  1. 标记(Mark):从根集遍历,标记所有可达对象
  2. 清除(Sweep):扫描堆,回收未标记对象

优点:解决循环引用 缺点:Stop the World,产生碎片

拷贝算法(Copying / Cheney)

将堆分为两个半区(A 和 B):

  1. 从 A 区分配内存
  2. A 区满时,将存活对象拷贝到 B 区
  3. 交换 A/B 角色

优点:分配快(地址递增),无碎片 缺点:空间利用率低(仅 50%)

标记-压缩(Mark-Compact)

  1. 标记阶段同标记-清除
  2. 压缩阶段将存活对象集中到堆的一端

优点:解决碎片问题,空间利用率高 缺点:压缩开销大

分代收集(Generational Collection)

基于”大部分对象短命”的观察,将堆分为多代:

  • 新生代:新分配对象,频繁回收
  • 老年代:存活多轮后晋升,较少回收
  • 永久代:类元数据等

GC 策略:新生代用拷贝算法,老年代用标记-清除或标记-压缩。

运行环境章节不考大题,GC 仅作概念了解。