针对大型应用程序的JVM性能调优

默认JVM参数不是运行大型应用程序的最佳选择。 来自在真实应用程序上进行调整的人的任何见解都会有所帮助。 我们在32位Windows机器上运行应用程序,默认使用客户机JVM。 我们添加了-server并将NewRatio更改为1:3(更大的年轻一代)。

您尝试过并发现有用的任何其他参数/​​调整?

[更新]我正在谈论的特定类型的应用程序是一个很少关闭的服务器应用程序,至少需要-Xmx1024m。 还假设已经分析了应用程序。 我正在寻找仅在JVM性能方面的一般指导原则。

周围有大量的信息。

首先,在调优JVM之前分析代码。

其次,仔细阅读JVM文档 ; 周围有很多“城市传说”。 例如,-server标志仅在JVM保持驻留并运行一段时间时才有用; -server“关闭”JIT / HotSpot,并且需要通过相同的路径进行多次传递才能启动。 另一方面,-server 减慢了JVM的初始执行速度 ,因为设置时间更长。

周围有好几本好书和网站。 例如,请参阅http://www.javaperformancetuning.com/

前言

背景

去过Java商店。 花了整整几个月专门在分布式系统上运行性能测试,主要应用程序是Java。 其中一些暗示Sun自己(然后是Oracle)开发和销售的产品。

我将介绍我学到的经验教训,一些关于JVM的历史,一些关于内部的讨论,一些参数的解释以及最后的一些调整。 试着保持这一点,以便你可以在实践中应用它。

Java世界的情况正在快速变化,因此自从去年我完成所有这些工作以来,其中一部分可能已经过时了。 (Java 10已经出来了吗?)

好的做法

你应该做什么:基准,Benchmark,BENCHMARK!

当您真正需要了解性能时,您需要针对您的工作负载执行真正的基准测试。 没有其他选择。

此外, 您应该监视JVM。 启用监控。 好的应用程序通常提供监视网页和/或API。 否则就有常见的Java工具(JVisualVM,JMX,hprof和一些JVM标志)。

请注意,通过调整JVM通常无法获得性能 。 它更像是“崩溃或不崩溃,找到过渡点” 。 这是关于知道当你为你的应用程序提供大量资源时,你可以始终如一地期待大量的表现。 知识就是力量。

表演主要取决于您的申请。 如果你想要更快,你必须编写更好的代码。

大多数时候你会做什么:生活在可靠的敏感默认值下

我们没有时间来优化和调整每个应用程序。 大多数时候,我们只会采用合理的默认设置。

配置新应用程序时要做的第一件事是阅读文档。 大多数严肃的应用程序都附带了性能调优指南,包括有关JVM设置的建议。

然后你可以配置应用程序: JAVA_OPTS: -server -Xms???g -Xmx???g

  • -server :启用完全优化(此标志现在在大多数JVM上是自动的)
  • -Xms -Xmx :设置最小和最大堆(两者总是相同的值,这是唯一要做的优化)。

干得好,你知道有关JVM的所有优化参数,恭喜! 这很简单:D

你不应该做什么,永远:

请不要复制您在互联网上找到的随机字符串,特别是当他们采取多行时:

 -server -Xms1g -Xmx1g -XX:PermSize=1g -XX:MaxPermSize=256m -Xmn256m -Xss64k -XX:SurvivorRatio=30 -XX:+UseConcMarkSweepGC -XX:+CMSParallelRemarkEnabled -XX:+UseCMSInitiatingOccupancyOnly -XX:CMSInitiatingOccupancyFraction=10 -XX:+ScavengeBeforeFullGC -XX:+CMSScavengeBeforeRemark -XX:+PrintGCDateStamps -verbose:gc -XX:+PrintGCDetails -Dsun.net.inetaddr.ttl=5 -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=`date`.hprof -Dcom.sun.management.jmxremote.port=5616 -Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.ssl=false -server -Xms2g -Xmx2g -XX:MaxPermSize=256m -XX:NewRatio=1 -XX:+UseConcMarkSweepGC 

例如,谷歌第一页上发现的这件事很明显。 有些参数指定了具有冲突值的倍数。 有些只是强制JVM默认值(最终默认来自2个JVM版本)。 有些是过时的,完全被忽略了。 最后,至少有一个参数是如此无效,以至于它在启动时会一直使JVM崩溃。

实际调整

你如何选择内存大小:

从您的应用程序阅读指南,它应该给出一些指示。 监控生产并随后进行调整。 如果您需要准确性,请执行一些基准测试

重要说明 :java进程最多需要10%的最大堆 。 X%开销是堆管理,不包含在堆本身中。

所有内存通常由启动时的进程预先分配。 您可以使用max heap ALL THE TIME查看进程。 这根本不是真的。 您需要使用Java监视工具来查看实际使用的内容。

找到合适的尺寸:

  • 如果它与OutOfMemoryException崩溃,则内存不足
  • 如果它没有与OutOfMemoryException崩溃,那就是内存太多了
  • 如果内存太多,但是硬件得到它和/或已经付费,那就是完美的数字,完成工作!

JVM6是青铜器,JVM7是金牌,JVM8是铂金……

JVM永远在改进。 垃圾收集是一件非常复杂的事情,有很多非常聪明的人在研究它。 它在过去十年中取得了巨大的进步,并将继续这样做。

用于提供信息。 它们是Oracle Java 7-8(HotSpot)和OpenJDK 7-8中至少4个可用的垃圾收集器。 (其他JVM可能完全不同,例如Android,IBM,嵌入式):

  • SerialGC
  • ParallelGC
  • ConcurrentMarkSweepGC
  • G1GC
  • (加上变种和设置)

[从Java 7开始,然后继续。 Oracle和OpenJDK代码是部分共享的。 GC(在大多数情况下)应该在两个平台上都相同。

JVM> = 7有很多优化并且选择了不错的默认值。 它按平台改变了一点。 它平衡了很多东西。 例如,决定是否启用多核优化,而不是CPU是否具有多个核心。 你应该让它做到。 请勿更改或强制GC设置。

让计算机为你决定(这就是计算机的用途)是可以的。 最好让JVM设置始终保持95%的最佳状态,而不是在所有方框上强制“总是8核心积极收集以减少暂停时间”,其中一半最终为t2.small。

例外 :当应用程序附带性能指南和特定调整时。 保留所提供的设置是完全可以的。

提示 :迁移到较新的JVM以从最新的改进中受益有时可以提供很好的提升而不需要太多努力。

特例:-XX:+ UseCompressedOops

JVM有一个特殊的设置,强制在内部使用32位索引(读取:指针式)。 这允许寻址4 294 967 295个对象* 8个字节地址=> 32 GB的内存。 (不要与REAL指针的4GB地址空间混淆)。

它可以降低整体内存消耗,并对所有缓存级别产生潜在的积极影响。

现实生活中的例子 :ElasticSearch文档指出,就内存中保存的实际数据而言,运行的32GB 32位节点可能相当于40GB的64位节点。

关于历史的一个注释 :在java-7之前的时代(甚至可能是java-6之前的版本),已知旗帜是不稳定的。 它已经在新的JVM中完美地工作了一段时间。

Java HotSpot™虚拟机性能增强

[…]在Java SE 7中,当未指定-Xmx且-Xmx值小于32千兆字节时,使用压缩oops是64位JVM进程的缺省值。 对于6u23发行版之前的JDK 6,请使用-XX:+ UseCompressedOops标志和java命令来启用该function。

请参阅 :JVM在手动调整方面再次亮相。 知道它仍然很有趣=)

特例:-XX:+ UseNUMA

非均匀存储器访问(NUMA)是用于多处理的计算机存储器设计,存储器访问时间取决于相对于处理器的存储器位置。 资料来源: 维基百科

现代系统具有极其复杂的内存架构,在内核和CPU上具有多层内存和高速缓存(私有和共享)。

很明显,在当前处理器中访问L2缓存中的数据比从另一个套接字一直到记忆棒要快得多。

我相信今天销售的所有多插槽系统都是NUMA设计,而所有消费者系统都不是。 numactl --show linux上的命令numactl --show检查您的服务器是否支持NUMA。

NUMA-aware标志告诉JVM优化底层硬件拓扑的内存分配。

性能提升可能很大(即两位数:+ XX%)。 实际上有人从“NOT-NUMA 10CPU 100GB”切换到“NUMA 40CPU 400GB”如果他不了解旗帜,可能会遇到[戏剧性]性能损失。

注意 :有讨论要检测NUMA并在JVM中自动设置标志http://openjdk.java.net/jeps/163

奖励 :所有打算在大型硬件硬件(即NUMA)上运行的应用程序都需要针对它进行优化。 它不是特定于Java应用程序。

走向未来:-XX:+ UseG1GC

垃圾收集的最新改进是G1收集器(阅读:Garbage First) 。

它适用于高内核,高内存系统。 绝对最少4核+ 6 GB内存。 它使用10次以上的数据库和内存密集型应用程序。

简短版本,在这些尺寸下,传统的GC面临着过多的数据需要立即处理,而暂停则失控。 G1在许多小部分中拆分堆,这些部分可以在应用程序运行时独立并行地进行管理。

第一个版本于2013年推出。它已经足够成熟,可以立即投入生产,但不会很快成为默认版本。 这对于大型应用程序来说值得一试。

请勿触摸:生成大小(NewGen,PermGen ……)

GC将内存分成多个部分。 (没有详细说明,你可以google“Java GC Generations”。)

我最后一次花了一个星期的时间在一个应用程序上尝试20个不同的代组合标志,达到10000次/秒。 我获得了-1%到+ 1%的惊人提升。

Java GC代是一个有趣的主题,可以阅读论文或撰写论文。 它们不是一个可以调整的东西,除非你是1%的人中的一员,他们可以在1%真正需要优化的人中投入大量时间获得微不足道的收益。

结论

希望这可以帮到你。 享受JVM的乐趣。

Java是世界上最好的语言和最好的平台! 去传播爱情:D

看这里(或者谷歌搜索热点调整) http://java.sun.com/javase/technologies/hotspot/gc/gc_tuning_6.html

在尝试调整虚拟机之前,您肯定想要分析您的应用。 NetBeans内置了一个很好的分析器,可以让你看到各种各样的东西。

我曾经有人告诉我,他们的应用程序中断了GC – 我查看了代码,发现他们从未关闭任何数据库查询结果,因此他们保留了大量的字节数组。 一旦我们关闭结果,时间从超过20分钟和GB内存到大约2分钟和非常少量的内存。 他们能够删除JVM调整参数,事情很开心。

我建议您在同时启用CPU采样和对象分配监视的情况下分析您的应用程序。 您将发现得到的结果非常不同,这有助于调整代码。 另外,尝试使用内置的hprof分析器,它也可以提供非常不同的结果。

一般来说,分析应用程序比JVM args有更大的不同。

回答这个问题的绝对最佳方法是在尽可能接近“生产”环境的情况下对应用程序执行受控测试。 使用-server,合理的起始堆大小以及最近JVM的相对智能行为很可能会比通常尝试的绝大多数设置表现得更好或更好。

这种广泛的概括有一个特定的例外:如果您在Web容器中运行,那么您很可能希望增加永久生成设置。

在32位Windows机器上的Java,您的选择是有限的。 根据我的经验,以下参数设置将影响应用程序性能:

  1. 内存大小
  2. 选择GC收集器
  3. 与GC收集器相关的参数

这将高度依赖于您的应用程序以及JVM的供应商和版本。 您需要明确您认为的性能问题。 您是否关注代码的某些关键部分? 你有没有想过应用程序? JVM是否花费太多时间进行垃圾收集?

我可能会从-verbose:gc JVM选项开始,观察垃圾收集是如何工作的。 很多时候,最简单的解决方法是使用-Xmx增加最大堆大小。 如果您学习解释-verbose:gc输出,它将告诉您几乎所有关于调整整个JVM的知识。 但单独做这件事并不会让错误调整的代码变得更快。 大多数JVM调优选项旨在提高垃圾收集器和/或内存大小的性能。

对于分析,我喜欢yourkit.com