压缩空间优化算法的核心目标是将存活对象集中到堆的一端以腾出连续空闲区域、消除碎片,其关键在于通过位图与分块偏移向量协同实现一趟扫描完成地址计算与就地滑动搬迁,避免额外存储开销和多趟扫描。

压缩空间优化算法,核心目标是把存活对象集中到堆的一端,腾出连续的空闲区域,同时避免额外内存开销和多次扫描。它不是单纯“压缩数据”,而是整理内存布局、消除碎片。
压缩阶段的关键逻辑
标记完成后,压缩不是靠复制到新区域,而是就地滑动。算法需预先计算每个存活对象的新地址——这个地址取决于它前面有多少字节被其他存活对象占据。因此,关键在于快速统计“某位置之前共占多少有效空间”。
- 传统做法(如Lisp2)为每个对象记一个转发地址,开销大且需额外存储空间
- 优化思路是放弃“逐对象记录”,改用“分块摘要”:把堆划分为固定大小的区块(如256字节),只为每个区块存一个偏移量,表示该区块内首个存活对象将挪到哪里
- 配合标记位图(每个字对应1比特),扫一遍就能算出任意区间内存活对象总大小,从而推导出具体对象的新地址
位图与偏移向量协同工作
标记位图只标对象起始和结束位置,不标记内部字节,大幅减少写操作;偏移向量按区块索引,条目数远少于对象数。两者结合,实现“一趟扫描完成地址计算+滑动搬迁”。
- 位图用于快速求和:比如想知第0~999字中存活了多少字,只需统计对应999个比特里有多少个1(起始/结束标记成对出现,可推断覆盖范围)
- 偏移向量提供基准:知道第n个区块的第一个存活对象要搬到地址X,再结合位图算出它在区块内的相对偏移,就能得出精确新地址
- 整个过程无需破坏对象原有字段,也不依赖堆内预留空间,真正实现零额外存储占用
为什么能减少扫描趟数
老式压缩算法常需3趟:标记→计算新地址→搬迁。Compressor类算法把地址计算和搬迁合并进同一遍正向扫描,靠提前构建好的位图和偏移向量实时查表+推算。
- 第一趟:标记 + 构建位图 + 统计各区块存活总量 → 填好偏移向量
- 第二趟:从堆底向上扫描,对每个存活对象,用所在区块偏移 + 位图前缀和,即时算出目标地址并搬运
- 全程无指针重写冲突,因所有新地址已确定,且搬迁方向与扫描方向一致(从前向后搬,不会覆盖未处理对象)
实际部署中的权衡点
这类算法对缓存友好,但对小对象密集场景更敏感——区块粒度选太大,偏移精度下降;太小,则偏移向量本身开销上升。典型配置是256–512字节区块,兼顾精度与空间效率。
- 适合老年代:对象大、存活率高,压缩收益明显,且移动成本相对可控
- 不适合频繁分配释放的小对象区:位图维护和偏移计算反而成为瓶颈
- 并发支持有限:位图更新和偏移向量生成仍需STW,但后续搬迁可部分并行化(需额外同步机制)











