背景
高性能应用构成了现代网络的支柱。LinkedIn 内部有许多高吞吐量服务来满足每秒成千上万的用户请求。为了获得最佳的用户体验,以低延迟响应这些请求是非常重要的。
例如,我们的用户经常使用的产品是 Feed —— 它是一个不断更新的专业活动和内容的列表。Feed 在 LinkedIn 的系统中随处可见,包括公司页面、学校页面以及最重要的主页资讯信息。基础 Feed 数据平台为我们的经济图谱(会员、公司、群组等)中各种实体的更新建立索引,它必须高吞吐低延迟地实现相关的更新。如下图,LinkedIn Feeds 信息展示:
为了将这些高吞吐量、低延迟类型的 Java 应用程序用于生产,开发人员必须确保在应用程序开发周期的每个阶段都保持一致的性能。确定最佳垃圾收集(Garbage Collection, GC)配置对于实现这些指标至关重要。
这篇博文将通过一系列步骤来明确需求并优化 GC,它的目标读者是对使用系统方法进行 GC 优化来实现应用的高吞吐低延迟目标感兴趣的开发人员。在 LinkedIn 构建下一代 Feed 数据平台的过程中,我们总结了该方法。这些方法包括但不限于以下几点:并发标记清除(Concurrent Mark Sweep,CMS(参考[2]) 和 G1(参考 [3]) 垃圾回收器的 CPU 和内存开销、避免长期存活对象导致的持续 GC、优化 GC 线程任务分配提升性能,以及可预测 GC 停顿时间所需的 OS 配置。
优化 GC 的正确时机?
GC 的行为可能会因代码优化以及工作负载的变化而变化。因此,在一个已实施性能优化的接近完成的代码库上进行 GC 优化非常重要。而且在端到端的基本原型上进行初步分析也很有必要,该原型系统使用存根代码并模拟了可代表生产环境的工作负载。这样可以获取该架构延迟和吞吐量的真实边界,进而决定是否进行纵向或横向扩展。
在下一代 Feed 数据平台的原型开发阶段,我们几乎实现了所有端到端的功能,并且模拟了当前生产基础设施提供的查询工作负载。这使我们在工作负载特性上有足够的多样性,可以在足够长的时间内测量应用程序性能和 GC 特征。
优化 GC 的步骤
下面是一些针对高吞吐量、低延迟需求优化 GC 的总体步骤。此外,还包括在 Feed 数据平台原型实施的具体细节。尽管我们还对 G1 垃圾收集器进行了试验,但我们发现 ParNew/CMS 具有最佳的 GC 性能。
1. 理解 GC 基础知识
由于 GC 优化需要调整大量的参数,因此理解 GC 工作机制非常重要。Oracle 的 Hotspot JVM 内存管理白皮书(参考 [4] )是开始学习 Hotspot JVM GC 算法非常好的资料。而了解 G1 垃圾回收器的理论知识,可以参阅(参考 [3])。
2. 仔细考量 GC 需求
为了降低对应用程序性能的开销,可以优化 GC 的一些特征。像吞吐量和延迟一样,这些 GC 特征应该在长时间运行的测试中观察到,以确保应用程序能够在经历多个 GC 周期中处理流量的变化。
- Stop-the-world 回收器回收垃圾时会暂停应用线程。停顿