为何Jonker标记-压缩(mark-compaction)算法无需额外存储空间?
为什么Jonker算法被普遍认为不需要额外空间?
核心区别从来不是「要不要存转发地址」,而是存地址用的空间是算法额外要求新增的常驻内存,还是复用系统里本来就已经存在、用完就会还原的临时空间。
先看Lisp 2算法的开销来源:
Lisp 2对内存布局的硬性要求是,每个对象的对象头必须专门预留一个完整槽位,唯一作用就是GC标记压缩阶段存对象的目标移动地址。这个槽位在GC不运行的时候完全没用,是为了支持算法硬加的固定开销——不管堆里对象是活是死、不管GC多久跑一次,每个对象从分配到回收,头里都要一直占着这一块空间,这就是实打实的、所有程序运行期间都要承担的额外内存成本。再看Jonker算法的指针穿线逻辑:
Jonker从头到尾都没要求给对象加新的常驻字段,它存地址用的全是本来就已经分配好的内存,用完就会恢复成原来的值,根本不会长期占空间:- 穿线过程中会临时覆写对象里原本就存在的指针字段:遍历存活对象链的时候,会把当前对象里存的、指向下一个对象的指针临时改成当前对象的转发地址,等遍历走到下一个节点时,就会把这个字段原来的指针值写回去。相当于把遍历路径上本来就有的指针当临时存储用,全程不碰新分配的内存。
- 书中提到的「对象头要有足够空间存地址,必要时可覆写其他数据」,这里覆写的也不是新增字段,是对象头里本来就有的、压缩阶段暂时用不上的原有字段(比如标记位旁边的预留位、暂时不需要读取的类型标识位),等压缩流程全部走完,这些字段都会被还原成原来的值,不会留下长期占用的空间。
GC领域说Jonker「不需要额外空间」是特定语境下的表述,指的是不需要为了支持算法,给所有对象增加永久存在的内存开销,不是说算法运行时完全不需要临时空间存地址。它的本质是用更高的实现复杂度换空间:书中提到的「必须能严格区分指针和非指针值」就是对应的代价——因为穿线要临时修改指针字段,如果把存整数、浮点数的非指针字段误当指针覆写,会直接损坏用户数据,这个实现门槛比Lisp 2高很多,但换回来的是没有常驻的对象头膨胀问题。
内容的提问来源于stack exchange,提问作者duwan.zq
相关产品推荐
相关产品推荐

