标记 - 整理(Mark-Compact)算法,又称为压缩(Compaction)算法,是一种垃圾回收机制,用于解决标记 - 清除算法导致的内存碎片问题。与标记 - 清除算法类似,标记 - 整理算法分为两个阶段:标记阶段和整理阶段。
标记阶段
在标记阶段,垃圾回收器遍历所有从根集合可达的对象,并标记这些对象。
整理阶段
在整理阶段,垃圾回收器会移动标记为活动的对象,将它们压缩到内存的一端,从而消除碎片并释放出一整块连续的未使用内存。
标记 - 整理算法的核心优势是它保留了所有存活对象,同时减少了内存碎片。但这个过程的缺点是需要更多的时间,因为移动对象涉及更多的内存操作。
我们可以用 JavaScript 来模拟标记 - 整理算法的基本步骤,但请注意,这个模拟是为教育目的,并不是实际的垃圾回收器实现。下面是用 JavaScript 来模拟的代码示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117
| class HeapObject { constructor(data) { this.data = data; this.marked = false; this.forwardingAddress = null; } }
class Heap { constructor() { this.objects = []; }
addObject(data) { const obj = new HeapObject(data); this.objects.push(obj); return obj; }
compact() { const liveObjects = []; const deadObjects = [];
for (const obj of this.objects) { if (obj.marked) { liveObjects.push(obj); } else { deadObjects.push(obj); } }
this.objects = []; for (const obj of liveObjects) { obj.forwardingAddress = this.addObject(obj.data); }
for (const obj of liveObjects) { obj.marked = false; }
return deadObjects.length; } }
class GC { constructor(heap) { this.heap = heap; this.roots = []; }
createObject(data) { const obj = this.heap.addObject(data); this.roots.push(obj); return obj; }
deleteObject(obj) { const index = this.roots.indexOf(obj); if (index !== -1) { this.roots.splice(index, 1); } }
mark() { for (const root of this.roots) { this.markRecursive(root); } }
markRecursive(obj) { if (!obj.marked) { obj.marked = true; } }
compact() { return this.heap.compact(); }
collectGarbage() { this.mark(); return this.compact(); } }
const heap = new Heap();
const gc = new GC(heap);
const obj1 = gc.createObject('obj1'); const obj2 = gc.createObject('obj2'); const obj3 = gc.createObject('obj3');
gc.deleteObject(obj2);
const collected = gc.collectGarbage(); console.log(`收集到 ${collected} 个不可达对象。`);
console.log(heap.objects.map(obj => obj.data));
|
在上面的示例中,我们定义了三个类:HeapObject
、Heap
和 GC
。
HeapObject
类代表堆中的对象,包括它的数据、一个标记位(用于标记清除),以及一个转发地址(在整理阶段使用)。Heap
类代表内存堆,有一个添加对象的方法和一个紧凑(压缩)方法。紧凑方法会将所有标记的对象移动到数组开始的位置,并更新它们的转发地址。GC
类是模拟垃圾收集器。它可以创建对象、删除对象的根引用、进行标记和整理。标记的递归方法假设对象间没有相互引用。在真实的情况下,这可能涉及到一个复杂的引用图。
当 collectGarbage
方法被调用时,它首先执行 mark
方法来标记所有可从根对象集合到达的对象。标记完成后,compact
方法会移动所有标记的对象并压缩堆,以减少内存碎片。
此模拟例示提供了对标记 - 整理(压缩)算法核心工作原理的基本了解。在真正的编程环境中,这个算法的实现细节会非常复杂,且会由底层语言运行时或虚拟机来完成,不会暴露给开发者。