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

11 garbage-collection

我正在读这个生成的html,(可能会过期,这是原始的ps文件.)

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

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

非常小的对象和大的稀疏堆==> GC通常更便宜,特别是对于线程

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

if (freeptr < someaddr) {
    *freeptr=ptr;
    ++freeptr; 
}
else
{
    freestuff();
    freeptr = freeptrroot;
}
Run Code Online (Sandbox Code Playgroud)

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

无论如何,我发布这个问题以获得进一步的见解.

Jon*_*rop 28

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

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

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

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

  4. 通过利用诸如不可变类型之类的额外静态信息.

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

  6. 通过使更高效的技术易于处理,例如通过从等待自由算法中消除手动内存管理的麻烦.

  7. 通过将解除分配推迟到更合适的时间或将其卸载到另一个核心.(感谢Andrew Hill的这个想法!)


kos*_*tja 15

使GC快速进行显式释放的一种方法是隐式解除分配:

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

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


Jan*_*dec 11

诀窍是,垃圾收集器的底层分配器可以比显式的更简单,并采取一些明确的不能的快捷方式.

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

  • @acidzombie:复制GC保留一个大块并使用它.指针仅*向上移动*.在解除分配时不会减少,特别是为了避免处理漏洞(除了可能在"顶部"被解除分配时作为优化,即在指针和解除分配的对象之间没有活动对象).当前区域耗尽时,所有活动对象都被复制到一个新区域(没有孔),指针在最后一个对象之后开始.(另见http://blogs.msdn.com/b/abhinaba/archive/2009/02/02/back-to-basics-copying-garbage-collection.aspx) (4认同)