垃圾回收调优

本页面探讨了 Java 虚拟机 (JVM) 的垃圾回收器对 Neo4j 性能的影响。在这种设置中,堆被分为老年代 (old generation)新生代 (young generation)。新对象会在新生代中分配,如果它们存活(被使用)的时间足够长,随后会被移动到老年代。

当某一代内存被填满时,垃圾回收器会执行一次回收,在此期间进程中的所有其他线程都会被暂停。新生代的回收速度很快,因为暂停时间与存活对象集 (live set) 相关。在老年代中,暂停时间大致与堆的大小相关。因此,理想情况下应调整堆的大小和配置,使事务和查询状态永远不会进入老年代。

堆大小通过 neo4j.conf 文件中的 server.memory.heap.initial_size(以 MB 为单位)设置进行配置。堆的初始大小由 server.memory.heap.initial_size 设置指定,或者使用 -Xms???m 标志指定,如果未指定,JVM 会根据启发式方法自行选择。JVM 会根据需要自动扩容堆,直至达到最大值。堆的扩容需要进行一次完整的垃圾回收循环。建议将初始堆大小和最大堆大小设置为相同的值。这样可以避免垃圾回收器因扩容堆而导致的暂停。

如果新生代太小,短生命周期的对象可能会过早地被移动到老年代。这被称为过早提升 (premature promotion),会因增加老年代垃圾回收循环的频率而拖慢数据库速度。如果新生代太大,垃圾回收器可能会认为老年代没有足够的空间来容纳所有预期从新生代提升到老年代的对象。这会将新生代垃圾回收循环转变为老年代垃圾回收循环,从而再次拖慢数据库速度。运行更多的并发线程意味着在给定的时间内可以进行更多的分配,进而特别增加了新生代的压力。

JVM 中的压缩指针 (Compressed OOPs) 功能允许将对象引用压缩为仅使用 32 位。该功能可以节省大量内存,但仅适用于不超过 32 GB 的堆。适用的最大大小因平台和 JVM 版本而异。可以使用 -XX:+UseCompressedOops 选项来验证系统是否可以使用压缩指针功能。如果无法使用,系统会在默认进程输出流中记录相关信息。

如何调整特定的垃圾回收算法取决于 JVM 版本和工作负载。建议在实际负载下进行数天或数周的垃圾回收设置测试。堆碎片化等问题可能需要很长时间才能显现出来。

为了获得良好的性能,首先应关注以下事项

  • 确保 JVM 没有花费太多时间执行垃圾回收。目标是拥有足够大的堆,以确保繁重/峰值负载不会导致所谓的“GC 抖动 (GC-trashing)”。当发生 GC 抖动时,性能可能会下降多达两个数量级。堆设置过大也可能损害性能,因此您可能需要尝试不同的堆大小。

  • Neo4j 需要足够的堆内存来处理事务状态和查询,并为垃圾回收器留出一些余量。由于堆内存需求非常依赖于工作负载,常见的堆内存配置从 1 GB 到 32 GB 不等。

编辑以下属性

表 1. neo4j.conf JVM 调优属性
属性名称 含义

server.memory.heap.initial_size

初始堆大小(以 MB 为单位)

server.memory.heap.max_size

最大堆大小(以 MB 为单位)

server.jvm.additional

额外的字面量 JVM 参数