堆管理问题
堆用于运行时动态分配内存(malloc/free、new/delete)。
分配策略
- 首次适应(First-Fit):选择第一个足够大的空闲块
- 最佳适应(Best-Fit):选择最接近请求大小的空闲块
内存碎片
- 内部碎片:分配块内部未使用的空间
- 外部碎片:空闲块被分割成多个小碎片,无法满足大块请求
手动内存管理的问题
- 悬空指针:释放后继续使用(use-after-free)
- 内存泄漏:不再使用的内存未释放
垃圾收集(GC, Garbage Collection)
GC 基本原理
- 根集(Root Set):所有栈帧和全局变量中的引用
- 可达性分析:从根集出发,可达对象为”存活”,不可达为”垃圾”
- Stop the World:GC 执行时暂停所有应用线程
引用计数(Reference Counting)
每个对象维护引用计数器,赋值时更新:
- 新增引用 → 计数器 +1
- 删除引用 → 计数器 -1
- 计数器归零 → 回收
优点:实现简单,实时回收 缺点:无法处理循环引用,性能开销大
标记-清除(Mark-Sweep)
两阶段算法:
- 标记(Mark):从根集遍历,标记所有可达对象
- 清除(Sweep):扫描堆,回收未标记对象
优点:解决循环引用 缺点:Stop the World,产生碎片
拷贝算法(Copying / Cheney)
将堆分为两个半区(A 和 B):
- 从 A 区分配内存
- A 区满时,将存活对象拷贝到 B 区
- 交换 A/B 角色
优点:分配快(地址递增),无碎片 缺点:空间利用率低(仅 50%)
标记-压缩(Mark-Compact)
- 标记阶段同标记-清除
- 压缩阶段将存活对象集中到堆的一端
优点:解决碎片问题,空间利用率高 缺点:压缩开销大
分代收集(Generational Collection)
基于”大部分对象短命”的观察,将堆分为多代:
- 新生代:新分配对象,频繁回收
- 老年代:存活多轮后晋升,较少回收
- 永久代:类元数据等
GC 策略:新生代用拷贝算法,老年代用标记-清除或标记-压缩。
运行环境章节不考大题,GC 仅作概念了解。