将Xms设置为Xmx的好处,竟然还可以减少GC转载
当我们启动应用程序时,会指定初始内存大小和最大内存大小。对于在JVM上运行的应用程序,通过“-Xms”和“-Xmx”参数指定初始和最大内存大小。如果Java应用程序在容器上运行,则通过“-XX:InitialRAMPercentage”和“-XX:MaxRAMPercentage”参数指定。大多数公司将初始内存大小设置为低于最大内存大小的值。与这种普遍接受的做法相反,将初始内存大小设置为与最大内存大小相同具有某些“酷”的优势。本篇就是介绍了将将Xms设置为Xmx会有什么优势!
1、可用性
如果你启动应用程序的初始堆大小为2GB,最大堆大小为24GB。当应用程序启动时,操作系统将为你的应用程序分配2GB内存。从那时起,随着应用程序开始处理新请求,将分配额外的内存,直到达到最多24GB。
如果你的应用程序内存消耗从2GB增长到24GB时,如果一些其他进程在你的设备中启动,并开始消耗内存。这种情况在生产/云环境中非常常见,特别是当你的应用程序与其他几个应用程序(例如自定义脚本、cron作业、监控代理…)一起运行时。
当这种情况发生时,你的应用程序将遇到:
1.‘java.lang.OutOfMemory错误:Java堆空间’
2.操作系统将以“内存不足:杀死进程 ”终止你的应用程序。
这意味着,你的应用程序将在交易中途崩溃。如果你的应用程序在启动期间以最大内存启动,你的应用程序将是安全的。操作系统只会终止内存消耗正在增长的新启动的脚本/cron作业,而不是在启动期间内存已完全分配的应用程序。
2、性能
初始堆大小和最大堆大小相同的应用程序比初始堆大小较低的应用程序性能相对较好。
这里有一个现实世界的案例研究:我们为研究采用了记忆密集型应用程序HeapHero。该应用程序处理非常大尺寸的二进制堆转储文件,并生成分析报告。在这个HeapHero应用程序中,我们反复分析了11GB的二进制文件,从而对操作系统施加内存压力。
我们进行了两个测试场景:
- 情景 1:我们设置初始堆大小为 2GB,最大堆大小设置为 24GB。
- 情景 2:我们已将初始堆和最大堆大小设置为 24GB。
在情景 1 中,我们观察到平均响应时间为 385.32 秒,而在情景 2 中,我们观察到平均响应时间为 366.55 秒。响应时间提高了5.11%。响应时间的改善有两个原因:
A、OS 的内存分配和处理分配;
B、GC暂停时间影响;
让我们在这里讨论它们:
A、OS 的内存分配和处理分配
当你为初始堆大小和最大堆大小设置不同大小时,JVM将不得不与操作系统协商,以便在需要时分配内存。同样,当应用程序对内存的需求在运行时下降时,操作系统将带走分配的内存。这种不断的分配和交易分配将为应用程序增加开销。
上图显示了场景1 JVM的分配和释放内存。从图表中,你可以注意到内存在不断波动。你可以看到堆大小在2GB到24GB之间波动。当应用程序处理堆转储时,内存拍摄高达24GB。处理后,内存会下降到2GB。当它再次处理新的堆转储时,内存会恢复到24GB。
上图显示了场景2 JVM在其生命周期内分配的内存。你可以看到没有波动。内存在启动期间从操作系统中保留,从那时起,没有波动。无论应用程序中的活动如何,它始终保持在24GB。这种行为有可能在一定程度上提高应用程序的性能。
B、GC暂停时间影响:
当垃圾收集运行时,它会暂停你的应用程序,这将对客户产生负面影响。我们使用GCeasy工具研究了两种场景的垃圾收集性能。以下是结果:
场景 | GC吞吐量 | 平均GC暂停时间 | GC 最大暂停时间 |
---|---|---|---|
情景 1:初始堆大小设置低于最大堆大小 | 96.59% | 99.2毫秒 | 5.23秒 |
场景2:初始堆大小设置为与最大堆大小相同 | 97.83% | 97.0毫秒 | 1.65秒 |
我们注意到GC吞吐量和GC暂停时间略有下降。在场景1中,GC吞吐量为96.59%,而在场景2中,GC吞吐量略好——97.83%。同样,在场景1中,Max GC的暂停时间为5.23秒,而在场景2中,暂停时间仅为1.65秒。
3、应用程序启动时间
如果你将初始堆大小设置为与最大堆大小相同,应用程序的启动时间也会更好。以下是甲骨文文档的摘录:
“如果初始堆太小,Java应用程序启动会变得缓慢,因为JVM被迫频繁执行垃圾收集,直到堆增加到更合理的大小。为了获得最佳启动性能,请将初始堆大小设置为与最大堆大小相同。”
4、成本
无论你将初始堆大小(-Xms)和最大堆大小(-Xmx)设置为相同值还是不同值,你支付给云托管提供商的计算成本($)都不会改变。假设你正在使用2.2倍的大型32G RHEL点播AWS ec2——美国西部(北加州)的实例,那么无论设置初始堆大小和最大堆大小的值如何,你最终都将每小时支付0.5716美元。云提供商不会根据你在该机器中使用的内存向你收费。它们仅根据你使用实例的时间收费。因此,将初始堆大小设置为低于最大堆大小不会节省成本。
结论
当你配置线程池或连接池时,将初始堆大小配置为小于最大堆大小是有意义的。在这些资源中,过度分配将产生不必要的影响,但内存并非如此。因此,如果你正在构建企业应用程序,你应该强烈考虑将初始堆大小和最大堆大小设置为相同的值。