垃圾收集器如何比显式内存释放更快?

时间:2011-04-28 08:43:26

标签: garbage-collection

我正在阅读html generated, (may expire,这是original ps file.

GC神话3:垃圾收集器总是比显式内存释放慢 GC神话4:垃圾收集器总是比显式内存释放更快。

这对我来说是一个很大的WTF。 GC如何比显式内存释放更快?当它释放内存/让它再次使用时,它本质上是否调用显式内存释放器?所以.... wtf ....它究竟意味着什么?

  

非常小的物体和大稀疏   heaps ==> GC通常更便宜,   尤其是线程

我仍然不明白。它就像说C ++比机器代码更快(如果你不理解这句话中的wtf请停止编程。让-1开始)。快速谷歌一个来源建议你有更多的内存时更快。我在想的是它意味着它根本不会打扰自由。当然可以很快,我编写了一个自定义分配器,它完成了这一点,在一个应用程序中根本不是免费的(void free(void*p){}),它不会释放任何对象(它只在终止时释放)并具有定义主要是在libs和类似stl的情况下。所以...我很确定GC也会更快。如果我仍然想要自由,我想我可以使用一个使用deque或它自己的实现的分配器,基本上

if (freeptr < someaddr) {
    *freeptr=ptr;
    ++freeptr; 
}
else
{
    freestuff();
    freeptr = freeptrroot;
}

我肯定会非常快。我已经回答了我的问题。永远不会调用GC收集器的情况是它会更快但是......我确信这不是文档的意思,因为它在测试中提到了两个收集器。我相信如果不管GC使用什么GC收集器甚至一次调用,那么相同的应用程序会更慢。如果它已知永远不需要自由那么可以使用一个空的自由身体,就像我有一个应用程序。

无论如何,我发布这个问题是为了进一步了解。

4 个答案:

答案 0 :(得分:28)

  

GC如何比显式内存释放更快?

  1. GC可以将指针碰撞分配到线程本地生成,然后依靠复制集合来处理疏散幸存者的(相对)不常见的情况。像malloc这样的传统分配器经常竞争全局锁和搜索树。

  2. 通过重置线程局部分配缓冲区而不是依次调用每个块上的free,GC可以同时解除分配许多死区块,即O(1)而不是O(n)。

  3. 通过压缩旧块,使更多的块适合每个缓存行。改进的局部性提高了缓存效率。

  4. 利用不可变类型等额外静态信息。

  5. 利用额外的动态信息,例如通过写屏障记录的数据改变堆的拓扑结构。

  6. 通过使更有效的技术易于处理,例如通过等待免费算法消除手动内存管理的麻烦。

  7. 将deallocation延迟到更合适的时间或将其卸载到另一个核心。 (感谢Andrew Hill的这个想法!)

答案 1 :(得分:15)

使GC更快,然后显式释放的一种方法是隐式解除分配:

堆在分区中划分,并且VM不时在分区之间切换(当分区变得太满时)例如)。活动对象被复制到新分区,并且所有死对象都没有被释放 - 它们只是被遗忘了。所以解除分配本身最终不会花费任何成本。这种方法的另一个好处是堆碎片整理是免费的奖励。

请注意这是对实际过程的一般描述。

答案 2 :(得分:11)

诀窍是,垃圾收集器的底层分配器可以比显式分配器简单得多,并采用一些明确的无法使用的快捷方式。

  1. 如果收集器正在复制(java和.net和ocaml以及haskell运行时和其他许多实际使用的那个),则释放是在大块中进行的,并且分配只是指针增量,并且每个对象幸存的收集都要支付成本。所以它更快,特别是当存在许多短暂的临时对象时,这在这些语言中很常见。
  2. 即使对于非复制收集器(如Boehm的收集器),对象被批量释放的事实在组合相邻的空闲块时节省了大量工作。因此,如果集合不需要经常运行,它可以更快地运行。
  3. 而且,很多标准库malloc / free实现都很糟糕。这就是为什么像umem这样的项目和像glib这样的库有自己的轻量级版本。

答案 3 :(得分:1)

尚未提及的一个因素是,当使用手动内存分配时,即使保证对象引用不形成周期,确定持有引用的最后一个实体何时放弃它也可能很昂贵,通常需要使用引用计数器,参考列表或其他跟踪对象使用的方法。这种技术在单处理器系统上并不算太差,其中原子增量的成本可能与普通增量成本基本相同,但它们在多处理器系统上的扩展非常糟糕,其中原子增量操作相对昂贵。