JVM性能调优涉及到很多方面的权衡,其中某一方面可能会极大地影响整体性能。因此,需要综合考虑所有可能的影响。理解并遵循一些基本原则和理论将使性能调优变得更加容易。为了更好地理解本文的内容,您必须满足以下先决条件:
了解 JVM 垃圾收集器
熟悉JVM性能监控常用工具
具备读取GC日志的能力
仅在必要且实用时才进行调优(JVM 性能调优并不能解决所有性能问题)
如果您不熟悉上述内容,建议您在继续阅读本文之前先阅读这些内容。
本文介绍了 JVM 性能调优,并展示了如何使用 JVM 的参数来执行应用程序调优。本文主要涉及以下内容:
JVM调优的一般流程
JVM调优的关键性能指标
重要的 JVM 调优原则
调整策略和示例
性能调优层
为了提高系统性能,我们需要从各个角度、各个层面对系统进行优化。
除了JVM调优之外,还有很多其他层需要优化。系统调优不仅仅包括 JVM 调优。相反,需要对系统进行整体调整以提高系统性能。本文仅介绍 JVM 调优。其他调整方面将在稍后讨论。
在进行JVM调优之前,假设项目的架构和代码已经调优或者是当前项目的最优架构和代码。这两个假设是JVM调优的基础,架构调优对系统性能的影响最为显着。我们不能指望一个架构有缺陷的应用程序能够通过仅执行 JVM 调优来实现质的飞跃。
另外,在调优开始之前,我们需要有明确的性能优化目标,了解当前的性能瓶颈。为了优化瓶颈,我们需要对应用程序进行压力和基准测试,并使用各种监控和统计工具来确认优化后的应用程序是否达到预期目标。
JVM调优过程
调优的最终目标是让应用程序以最低的硬件消耗成本获得更大的吞吐量。JVM 调优也不例外。JVM 调优主要涉及优化垃圾收集器以获得更好的收集性能,从而使运行在虚拟机上的应用程序能够在使用更少的内存和体验更低的延迟的同时获得更大的吞吐量。请注意,更少的内存/更低的延迟并不一定意味着更少/更低的内存/延迟,性能就越好。这是关于最优选择。
性能指标
为了查找和评估性能瓶颈,我们需要了解一些性能指标的定义。对于 JVM 调优,我们需要了解以下三个定义,并将这些指标作为我们评估的基础:
吞吐量:它是重要指标之一。吞吐量是指垃圾收集器允许应用程序达到的最高可能性能,而不考虑垃圾收集引起的暂停时间或内存消耗。
延迟:延迟衡量的是垃圾收集导致的暂停时间减少了多少,以避免应用程序在运行过程中发生振动。
内存使用量:指垃圾收集器顺利运行所需的内存量。
这三个属性中任何一个的性能提升几乎是以其他一两个属性的性能损失为代价的。应用程序业务需求决定了一个或两个属性对于应用程序的重要性。
性能调优原则
在调优过程中,以下三个原则可以帮助我们实现更简单的垃圾收集调优,以满足期望的应用程序性能要求。
Minor GC收集原则:每次Minor GC应该收集尽可能多的垃圾对象,以减少应用程序的Full GC频率。
GC内存最大化原则:在解决吞吐量和延迟问题时,垃圾收集器使用的内存越大,垃圾收集越高效,应用也越流畅。
GC调优“三取二”原则:我们应该只调整三个性能属性中的两个,而不是全部三个属性:吞吐量、延迟和内存使用率。
性能调优过程
JVM 调优涉及持续的配置优化和基于性能测试结果的多次迭代。在满足每个期望的系统指标之前,前面的每个步骤可能会经历多次迭代。在某些情况下,为了满足特定的指标,之前的参数可能需要多次调整,需要重新测试之前的所有步骤。
此外,调优通常从满足应用程序的内存使用要求开始,然后是延迟和吞吐量。调整应遵循以下步骤顺序。我们不能颠倒这些调整步骤的顺序。以下部分将使用示例来详细说明每个调整步骤。
对于运行JVM,我们直接选择Server模式,这是JDK 1.6之后官方推荐的模式。
我们使用JDK 1.6-1.8中默认的并行收集器作为垃圾收集器。(用于 parallelGC 年轻代,parallelOldGC 用于老年代。)
确定内存使用情况
在确定内存使用情况之前,我们需要了解两件事:
应用程序运行阶段
JVM内存分配
运营阶段
我将应用程序的运行分为以下三个阶段:
初始化:JVM加载应用程序并初始化应用程序的主要模块和数据。
稳定性:应用程序已经运行了很长时间,并接受了压力测试。各项性能参数均处于稳定状态。核心功能已通过 JIT 编译执行和预热。
摘要:在最后的总结阶段,进行一些基准测试,生成相应的报告。我们不必关注这个阶段。
内存使用情况和活动数据的大小应在应用程序稳定阶段确定,而不是在项目启动阶段确定。在解释如何确定内存使用情况之前,我们先看一下 JVM 内存分配。
JVM 内存分配和参数
3
JVM 的主要堆空间由年轻代、老年代和永久代组成。年轻代大小、老年代大小和永久代大小组成了总堆大小。具体的对象提升方法这里不讨论。现在,让我们看看以下 JVM 命令如何指定堆大小。如果不使用以下参数指定堆大小,虚拟机会自动选择一个合适的值,该值可以根据系统开销自动调整。
4
如果考虑性能开销,请尽可能将永久代的初始大小和最大大小设置为相同的值,因为只有FullGC可以实现永久代的大小调整。
计算活动数据的大小
要计算活动数据的大小,请按照下列步骤操作:
5
正如前面提到的,活动数据大小应该通过自应用程序稳定阶段开始以来长期处于活动状态的数据占用了多少 Java 堆空间来衡量。
计算活跃数据大小时,请务必满足以下要求:
执行测试时,不要手动设置启动参数,而是使用默认的 JVM 参数。
确保Full GC发生时应用程序处于稳定状态。
使用默认的JVM启动参数是为了观察应用程序处于稳定阶段时所需的内存使用情况。
应用程序何时处于稳定阶段?
当应用承受足够的压力后,只有在生产环境的业务高峰时达到满足业务需求的工作负载时,应用才处于稳定阶段,并在高峰后保持稳定。因此,要确定应用程序是否达到稳定阶段,压力测试是必不可少的。如何对应用程序进行压力测试不在本文讨论范围内。这个问题后面会单独写一篇文章来解释。