我已经了解了不同种类的GC以及它们的工作原理。所有这些都涉及遍历可能被回收的内存集,但我读过的内容并没有给出任何关于如何实际完成的指示。像下面这样的东西会不合时宜吗?
void* myAlloc(size_t size)
{
if (needToGc())
gc();
void* obj = malloc(size);
if (!obj)
outOfMemory(); // or maybe GC and try once more
*alloced = obj;
*alloced = *alloced + 1;
return obj;
}
void gc()
{
// go through memory pointed to in `alloced`
}
我怀疑是这样,但这是唯一让我想到的事情......
答案 0 :(得分:1)
垃圾收集器有很多种,具体取决于要求(内存开销,延迟,数据布局......)。我的一些答案可能不适用于一些复杂的收藏家。
首先,不需要needToGc
:如果malloc
失败,则会触发垃圾收集器。
关于如何遍历内存,垃圾收集器的任务是将正在使用的内存与未使用的内存分开,并回收后者。基本原则是如果可以从程序访问内存,则使用内存。确定如下:
某些 root 对象被视为可访问。例如,堆栈上的任何内容都是可访问的,并且可以访问任何全局变量。
如果可到达的对象指向另一个对象,则该另一个对象也可以访问。
遗留下来的任何东西都无法到达,因此被视为未使用且可回收。
当触发垃圾收集器时,它会遍历所有根对象,并且每个对象递归遍历可到达的对象。收集器遍历所有可到达的对象后,它将回收未遍历的对象。
这种遍历的简单技术称为标记和扫描。每个对象都包含一个最初为0的标记位。当遍历函数到达一个对象时,它会查看标记位:如果标记位为1,则该对象已被遍历;如果标记位为0,则遍历函数将其设置为1,并在当前对象指向的每个对象上递归调用自身。标记阶段包括为每个根对象调用遍历函数。接下来是扫描阶段,它遍历所有已分配的对象:如果对象仍然标记为0,则释放它;否则其标记重置为0。
那里的大多数垃圾收集器都基于标记和扫描,但通常会有相当大的复杂性。
有关更多信息和其他指示,请参阅wikipedia article。