标记-清除算法仅清空死亡对象空间而不移动存活对象,导致内存碎片化、空闲块孤立且无法合并,尤其老年代易因长期运行而严重碎片化。
因为它只“清空位置”,从不“挪动东西”。
标记-清除算法在清除阶段不做任何移动操作:所有存活对象保持原有内存地址不变,仅把死亡对象占用的空间标记为空闲。这些空闲空间就像散落在固定骨架之间的缝隙,彼此孤立、大小随机。时间一长,堆就变成蜂窝状——空洞密布,但无法拼接。
算法本身不检查相邻空闲区域是否可合并。两个紧挨着的64KB空闲块,在空闲链表里仍是两条独立记录,不会自动合成128KB大块。分配器遍历时只能逐个匹配,根本看不到“连续性”。
循环中反复创建小对象(如临时String、HashMap.Entry),会不断在存活对象之间切分、填充、再释放,导致小碎片快速累积。就像往固定格子的抽屉里不停塞不同尺寸的纸片,越塞越乱,最终连一张完整A4都铺不开。
年轻代用复制算法,每轮Minor GC都会把存活对象集中搬运,天然归整;老年代对象多、存活率高、没备用空间可复制,只能就地清理。CMS这类纯标记-清除收集器长期运行后,碎片雪球越滚越大,大对象一来就卡住。