我怎样才能在multithreading环境中实现垃圾收集?

我应该如何在由多个线程或进程组成的程序中执行垃圾回收?

我怎样才能从每个线程和进程扫描堆栈?

每个进程是否需要自己的垃圾收集程序? 在一个单独的线程/进程中从实际程序中运行垃圾收集器是一个好主意吗?

你必须每个地址空间收集一次。 多个线程在同一个地址空间中运行,所以需要一个GC来处理这个问题。 对于产生不同进程的程序,每个程序都在自己的地址空间中运行,每个进程可以有一个GC。

在多线程的情况下,运行GC作为另一个线程是有意义的。 这样,您可以摆脱该线程的优先级,以确保整个程序的大体平稳运行。 对于一个单线程的进程来说,最简单的办法是把一个GC挂钩到“正常的”内存管理例程(特别是分配)中,但是你可以有两个线程,一个是原始进程的线程,一个是GC线程,再次确保半顺畅的表现。

停止世界收藏家是最简单的标记和扫描,标记和紧凑,停止和复制,你的名字。 在单线程程序中挂钩到内存管理例程的GC本质上就是“停止世界”。 在多线程的情况下,GC线程可以被线程调度器赋予特殊的权限(一旦决定运行,就使其不可中断),从而允许您从这样的特权线程运行“停止世界”的GC。

如果GC可能被mutator(也就是程序的其余部分)中断,特别是因为它只是另一个根本就没有特别处理的线程,你需要一个GC来处理mutator的干扰。 在这种情况下,您正在查看增量式GC。 IGC可以用于单线程,挂钩到内存管理的例程设置中,在这种情况下IGC可能被中断,例如超时,以确保整个系统的平稳运行; 它也可以在多线程系统中使用,在这种系统中,它只是与其他线程竞争运行时间。

如何找到一个程序的堆栈或一个程序的所有线程我不能告诉你,但是这些结构的布局应该记录在每个操作系统上。 抓住Boehm GC并扫描提示来源可能是有意义的。

当琼斯/林斯正在运送给你时,花一些时间在http://www.memorymanagement.org/ 。 更多的信息,正如Charly Martin上面所说的那样,Sun的员工在垃圾收集方面做了一些惊人的研究和开发,就像IBM Jikes RVM团队的成员和同事一样。

编辑:在阅读您对查理·马丁的评论之后,让我给你一个更加深入的建议:把Boem GC连接到你的系统并完成它 。 编写一个垃圾收集器很容易。 编写一个正确,高效,快速,行为良好,调优和强大的垃圾收集器几乎是不可能的。 使用现有的GC,并转到您的项目的有趣的部分。 不要被GC困住,或者更糟糕的是,执行不好的GC会让你烦恼不已。

大多数GC都称为“停止世界”GC – 在某些预定义点(“Gc点” – 例如调用点,跳转,返回)中,每个线程将检查是否存在某个想要执行GC循环的其他线程。 一旦所有线程都停止,GC循环将运行。

当然,还有其他的可能性 – 实时的,增量的,并发的(还有更多)地理信息系统也存在 – 搜索网络(你将主要找到已发表的论文),或者只是买一本关于地理信息系统的书

至于堆栈扫描有几种方法:

  • 精确扫描:

    • 标签堆栈 – 你基本上保持2栈 – 一个值和一个“标签”。 什么标签取决于你需要什么,但它将主要是“参考”/“不参考”的标志

    • 没有标签的堆栈 – 基本上如果你有一个严格的语言类型,你可以在每个时间点(但更常见的是在每个“GC点”)知道堆栈的类型。 我会给你一个简单的解释器的例子(我刚刚做了):

no-return function XY (int): load_param 1 ipush 1 iadd call Z (assume: int function Z (int, int)) new some_object call Y 

如果我们定义我们的GC poins为call / new,那么可能有3个点需要知道我们的堆栈类型(在XY函数入口堆栈被认为是“空”):

  • 调用Z – load_param加载一个int参数,ipush在栈上推入一个int – 2 int,没有任何东西给我们的GC在这里做
  • 新的 – 调用Z从堆栈中使用2个整数,并放置一个int
  • 调用Y – 新放置一个引用,所以现在我们有一个int和一个引用,GC已经知道那个引用

请注意,我说堆栈在函数入口处是“空的” – 它确实不是,但你可以分别分析每个函数,然后向上移动“调用堆栈”(你有一个返回指针的地方,所以你知道你在哪里必须返回 – 返回-1是一些电话,你也可以得到一个图像的堆栈。重复,直到你到达顶部)。

有两种方法可以记住这个信息(对于一个没有标签的堆栈):

  • 为每个GC点生成一个表格
  • 为每个GC点生成一个代码片段(代码片段本身将标记引用的对象)

至于你什么时候收集这些信息,可能是预编译的或者是及时的。

这也可以应用于机器堆栈,但事情有点复杂,因为你可能也需要跟踪寄存器。

你应该可以在网上找到一些关于无标签堆栈的好文章。

还有进一步的修改,你添加例如。 “活泼”的信息给你的数据(好的,在栈上有一个引用,但是如果没有代码使用它的指令流,我可以把它当作无法访问)

  • 保守的收集(而不是精确的扫描) – 你问自己“在堆栈上的这个值,解释为一个指针,一个引用”,如果它是那么“活着”。 这当然可以泄漏,如果有例如。 一个看起来像指针的整数(如果整数变化,内存将被释放,但也可能永远是韭菜)。 大多数c / c ++收藏家都是这样实现的,所以如果你好奇的话,就可以在那个方向上搜索。

每个进程是否都需要它自己的垃圾回收例程?

没有什么需要这个,但我会说这是常见的 – 为了简单起见,我会为每个进程有一个不同的GC实例(但只有一个用于所有线程)。 我想在这些进程之间可能会有一个共享内存分配器 – 唯一的好处是我可以更好地管理内存碎片(因为你控制更多的内存),但复杂性(inteproces通信/同步 – 糟糕),共享数据量缺乏独立性成为问题。 我在这里猜测,我对这种类型的GC(或者即使存在)也没有真正的经验 – 这对我来说似乎是常识。

在一个单独的线程/进程中从实际程序中运行垃圾收集器是一个好主意吗?

这得看情况。 将它保留在另一个线程中应该是一个好主意,但是这样做取决于你是否想让GC简单(“停止世界” – 所有其他线程都被暂停,所以它几乎不重要哪个线程执行我们的GC,但是如果它有它自己的线程则更好),还是你有特殊的要求(例如你的线程是实时的,不应该停止很长一段时间,那么你将有一个不同的GC线程并使用实时/增量GC算法)。

这一切都取决于你需要什么, 但是不管你做什么,记住 – 尽可能简单。

我几乎忘记了,从零开始编写自己的GC有一些很好的选择 – 例如。 看到LLVM ,他们声称“利用这些工具,只需要大约100行C ++代码,就可以直接为运行时发布类型精确的堆栈映射。 (只有预编译的代码,没有代码JIT生成支持)。 另外,你可以看看一些Java虚拟机的代码(例如phoneME高级虚拟机(CVM),而且据我所知,kaffe是非常可读的)。

免责声明:我曾经(作为一个学生项目)实施了一个精确的,停止世界,无标签,标记和扫描的GC – 这里写的所有内容都是我从经验中记住的结果,应该是正确的,但它可能不是“最佳实践”。 更正是受欢迎的。

你为什么要做自己的 GC? 或者你只是想了解一般的GC? Hrvoje推荐的Jones和Lin书很好, 维基百科的文章看起来不错。

Java 1.4.2之后的Java提供了一种增量混合GC,不会强制“停止世界”的停顿; 我们需要在Java ME世界中处理面向设备的Java。 在Sun Java网站上有一个相当不错的扩展论文 。

大多数每个GC环境(例如Java和.NET)都在垃圾回收期间暂停所有线程。

这是Maoni Stephens的博客 。 她是.Net GC目前的主要开发人员。

这是一篇关于并发GC的文章,讲述了.Net GC在这种情况下如何工作。