了解热点 JVM 进程的内部碎片属性

对于堆上和堆外分配。堆上 - 在三个主要垃圾回收器的上下文中:CMS,Parallel Old和G1。

我所知道的(或认为我知道的)到现在:

  • 所有对象(堆上)分配都向上舍入到 8 个字节边界(或更大的 2 次幂,由 配置)。-XX:ObjectAlignmentInBytes
  • G1
    • 对于小于区域大小(1 到 32 MB,可能大约在堆大小 / 2048)的堆上分配,没有内部碎片,因为没有必要,因为分配器永远不会“填补漏洞”。
    • 对于大于区域大小的分配,它会将分配向上舍入为区域大小。即分配区域大小+1字节是非常不吉利的,它浪费了近50%的内存。
  • 对于CMS,我找到的唯一相关信息是

    自然的旧空间PAB模仿索引自由列表空间的结构。每个线程预先分配一定数量的块,每个大小小于 257 个堆字(从全局空间分配的大块)。

    http://blog.ragozin.info/2011/11/java-gc-hotspots-cms-promotion-buffers.html。据我所知,所称的“全球空间”是主要的旧空间。

问题:

  • 上述说法是否正确?
  • CMS中主要旧空间的碎片化特性是什么?如果分配超过“257 堆字”呢?
  • 如何使用并行旧 GC 管理旧空间?
  • 热点 JVM 是使用系统内存分配器进行堆外分配,还是使用特定分配器重新管理它?

UPD。讨论线索:https://groups.google.com/forum/#!topic/mechanical-sympathy/A-RImwuiFZE


答案 1
  • 据我所知,上面的陈述是正确的,尽管CMS上的位缺少很多上下文来解释它。
  • CMS容易出现碎片化(在其运行CMS的旧空间中),这是其主要缺陷之一。如果它碎片太多,它可能偶尔需要停止世界并进行完整标记和(滑动)压缩以删除碎片,这会导致应用程序中出现大量暂停。正是这个缺陷经常被引用为G1开发的原因。一些系统(例如HBase)故意使用固定大小的块进行大部分分配,以防止或显着减少碎片CMS,以避免长时间的停止世界暂停。
  • ParallelOldGC(或一般的“Old GC”)不会分段。对象被永久保留在旧堆中,当它空间不足时,将运行一个完整的标记和紧凑的循环。它可以比任何其他分配器更快地完成此完整 GC,但典型运行时间为每 2 GB 堆 1 秒,对于大型堆或延迟敏感型应用程序,这可能太长。
  • Hotspot 根据用途使用了各种策略进行堆外分配。分配本机字节缓冲区不同于它自己为编译代码或分析数据分配的缓冲区。我不能在这里权威地回答任何细节,但我只能假设其中大部分不使用系统分配器,否则Hotspot的性能不会像它那样好。此外,有一些参数可以调整来控制一些这样的空间,例如,这表明这样的内存区域是通过间接而不是直接通过系统分配器来管理的。简而言之,如果系统分配器直接用于热点中的任何细粒度分配,我会感到非常惊讶。-XX:ReservedCodeCacheSize

答案 2