Java 8/9带来了对-XX:+UseCGroupMemoryLimitForHeap(带有-XX:+UnlockExperimentalVMOptions)的支持。这设置-XX:MaxRAM为cgroup内存限制。默认情况下,JVM会分配大约25%的最大RAM,因为-XX:MaxRAMFraction默认值为4。
-XX:+UseCGroupMemoryLimitForHeap
-XX:+UnlockExperimentalVMOptions
-XX:MaxRAM
-XX:MaxRAMFraction
例:
MaxRAM = 1g MaxRAMFraction = 4 JVM is allowed to allocate: MaxRAM / MaxRAMFraction = 1g / 4 = 256m
对于仅由单个JVM进程组成的部署,仅使用配额的25%似乎是浪费。因此,现在人们开始设置-XX:MaxRAMFraction=1,因此从理论上讲,JVM可以使用MaxRAM的100%。
-XX:MaxRAMFraction=1
对于1g的示例,这通常导致堆大小约为900m。这似乎有点高-JVM或其他东西(如远程Shell或进程外任务)没有太多可用空间。
那么,此配置(-XX:+UnlockExperimentalVMOptions -XX:+UseCGroupMemoryLimitForHeap -XX:MaxRAMFraction=1)是否对产品甚至最佳实践来说都是安全的?或者我应该还是手拿起-Xmx,-Xms,-Xss等?
-XX:+UnlockExperimentalVMOptions -XX:+UseCGroupMemoryLimitForHeap -XX:MaxRAMFraction=1
-Xmx
-Xms
-Xss
我们进行了一些简单的测试,结果表明该设置-XX:MaxRAM=$QUOTA和-XX:MaxRAMFraction=1导致在负载下被杀死的容器。JVM分配了超过900M的堆,这太多了。-XX:MaxRAMFraction=2似乎很安全。
-XX:MaxRAM=$QUOTA
-XX:MaxRAMFraction=2
请记住,您可能希望为其他过程留出空间,例如docker exec在容器中获取调试shell()或诊断信息。
docker exec
编辑:我们已经在一篇文章中写下了我们所学的内容。货币报价:
TL’DR: Java内存管理和配置仍然很复杂。尽管从Java 9 / 8u131开始,JVM可以读取cgroup内存限制并相应地调整内存使用量,但这并不是万灵药。您需要知道-XX:+UseCGroupMemoryLimitForHeap它的作用,并且需要为每个部署微调一些参数。否则,您可能会浪费资源和金钱,或者在最坏的时间杀死集装箱。-XX:MaxRAMFraction=1尤其危险。Java 10+带来了许多改进,但仍需要手动配置。为了安全起见,请对您的东西进行负载测试。
和
最优雅的解决方案是升级到Java 10+。Java 10弃用-XX:+UseCGroupMemoryLimitForHeap(11)并引入-XX:+UseContainerSupport(12),以取代它。它还引入了-XX:MaxRAMPercentage(13),其值在0到100之间。这允许对允许JVM分配的RAM数量进行精细控制。由于+UseContainerSupport默认情况下处于启用状态,因此所有操作均应立即可用。
-XX:+UseContainerSupport
-XX:MaxRAMPercentage
+UseContainerSupport
编辑#2:我们写了更多有关-XX:+UseContainerSupport
引入了Java 10 +UseContainerSupport(默认情况下启用),它使JVM在容器环境中使用合理的默认值。自8u191以来,此功能已反向移植到Java 8,这可能允许大量野生Java部署正确配置其内存。