qbit
  • 149

JVM 参数调优(qbit)

前言

  • JVM 的参数有好几百个,听着有点吓人,好在最常用的参数只有两个,其他绝大多数参数都无需调整。可以参考廖雪峰的文章: JVM调优的正确姿势
-Xms8g 
-Xmx8g

打印 JVM 参数

  • 打印 JVM 参数初始值
λ java -XX:+PrintFlagsInitial
  • 打印 JVM 参数最终值
λ java -XX:+PrintFlagsFinal 2> nul
  • 打印被修改过的 JVM 参数
# 输出经由人工换行
λ java -XX:+PrintCommandLineFlags 2> nul
-XX:InitialHeapSize=266579392 
-XX:MaxHeapSize=4265270272 
-XX:+PrintCommandLineFlags 
-XX:+UseCompressedClassPointers 
-XX:+UseCompressedOops 
-XX:-UseLargePagesIndividualAllocation 
-XX:+UseParallelGC
  • 在 java 代码里面打印
import java.lang.management.ManagementFactory;
import java.lang.management.MemoryMXBean;
private void printJvm() {
    MemoryMXBean memorymbean = ManagementFactory.getMemoryMXBean();
    System.out.println("堆内存信息: " + memorymbean.getHeapMemoryUsage());
    System.out.println("方法区内存信息: " + memorymbean.getNonHeapMemoryUsage());

    List<String> inputArgs = ManagementFactory.getRuntimeMXBean().getInputArguments();
    System.out.println("\n####################运行时设置的JVM参数####################");
    System.out.println(inputArgs);

    System.out.println("\n####################运行时内存情况#######################");
    long totle = Runtime.getRuntime().totalMemory();
    System.out.println("总的内存量 [" + totle + "]");
    long free = Runtime.getRuntime().freeMemory();
    System.out.println("空闲的内存量 [" + free + "]");
    long max = Runtime.getRuntime().maxMemory();
    System.out.println("最大的内存量 [" + max + "]");
}

JVM 内存模型

  • Java 7

jvm7

  • Java 8
堆内的 PermGen 被移除
新增 Metaspace 替代 PermGen
PermGen 不在堆中
PermGen 直接占用的本地内存(NativeMemory)

jvm8

常用参数

-Xms: 初始堆大小
-Xmx: 最大堆大小
-XX:NewSize: 年轻代初始化内存的大小(注意:该值需要小于-Xms的值)
-XX:MaxnewSize: 年轻代可被分配的内存的最大上限(注意:该值需要小于-Xmx的值)
    从 JKD1.4 开始,MaxnewSize 是通过 NewRatio 计算出来的
-Xmn: 对-XX:newSize、-XX:MaxnewSize两个参数同时进行配置(JDK1.4之后才有该参数)
    官方推荐为对大小的 3/8,即 1/4 到 1/3 之间
-XX:NewRatio: 设置老年代和年轻代的比值
   若 -Xmn 已指定,则 OldSize = HeapSize - NewSize,无需再按比例计算。
   例如 NewRatio 为 3,表示 老年代/年轻代 = 3,年轻代占整个堆内存大小的 1/4
  • 对于年轻代的堆内存大小,默认情况下是通过 NewRatio(2) 计算出来的,即占用 1/3;在配置 Xmn 后,会覆盖默认的通过 NewRatio 计算出来的年轻代堆大小值
# 摘录的部分的输出行
# MaxHeapSize/MaxNewSize = 4265607168/1421869056 = 3
λ java -XX:+PrintFlagsFinal
    uintx NewRatio = 2                   {product}
    uintx MaxNewSize    := 1421869056    {product}
    uintx MaxHeapSize   := 4265607168    {product}
  • 一般 -Xms、-Xmx 两个参数会配置相同的值(优点:能够在Java垃圾回收机制清理完堆区后不需要重新分隔计算堆区的大小而浪费资源)。

CompressedOops

压缩普通对象指针

compressed ordinary object pointers,压缩普通对象指针
CompressedOops,可以让跑在64位平台下的JVM,不需要因为更宽的寻址,而付出Heap容量损失的代价。
它的实现方式是在机器码中植入压缩与解压指令,这可能会给 JVM 增加额外的开销。

零基压缩优化

Zero Based Compressd Oops
零基压缩是针对压解压动作的进一步优化。 
它通过改变正常指针的随机地址分配特性,强制堆地址从零开始分配(需要 OS 支持),
从而进一步提高了压解压效率。
要启用零基压缩,你分配给JVM的内存大小必须控制在 4G 以上,32G 以下。

中场小结

(0, 2GB]      Compressed Oops mode: 32-bit
[2GB, 26GB]   Compressed Oops mode: Zero based,26G 不是确切值,视系统而定
(26GB, 32GB)  Compressed Oops mode: Non-zero disjoint base,32G 不是确切值,视系统而定
[32GB, )      CompressedOops 失效,32G 不是确切值,视系统而定

CompressedOops

检查 CompressedOops 阈值

  • 下面测试的粒度为 GB,也可以到 MB
  • 以下命令适用于 JDK7/JDK8
# 测试环境
Windows 10
JDK7
# CompressedOops 阈值
# 32 G,false 表示超过了阈值
> java -Xmx32g -XX:+PrintFlagsFinal 2> nul | findstr UseCompressedOops
     bool UseCompressedOops    = false    {lp64_product}
# 31G,true 表示在阈值之内
> java -Xmx31g -XX:+PrintFlagsFinal 2> nul | findstr UseCompressedOops
     bool UseCompressedOops    := true    {lp64_product}
# zero based Compressed Oops 阈值
# 32G,未启用压缩
> java -server -Xms32G -Xmx32G -XX:+UseConcMarkSweepGC -XX:+UnlockDiagnosticVMOptions -XX:+PrintCompressedOopsMode -version
java version "1.7.0_21"
Java(TM) SE Runtime Environment (build 1.7.0_21-b11)
Java HotSpot(TM) 64-Bit Server VM (build 23.21-b01, mixed mode)

# zero based Compressed Oops 阈值
# 30G,Non-zero
> java -server -Xms30G -Xmx30G -XX:+UseConcMarkSweepGC -XX:+UnlockDiagnosticVMOptions -XX:+PrintCompressedOopsMode -version
Protected page at the reserved heap base: 0x000000007fff0000 / 65536 bytes
heap address: 0x0000000080000000, size: 30802 MB, Compressed Oops with base: 0x000000007ffff000
java version "1.7.0_21"
Java(TM) SE Runtime Environment (build 1.7.0_21-b11)
Java HotSpot(TM) 64-Bit Server VM (build 23.21-b01, mixed mode)

# 30G,Zero based
> java -server -Xms29G -Xmx29G -XX:+UseConcMarkSweepGC -XX:+UnlockDiagnosticVMOptions -XX:+PrintCompressedOopsMode -version
heap address: 0x00000000bae00000, size: 29778 MB, zero based Compressed Oops
java version "1.7.0_21"
Java(TM) SE Runtime Environment (build 1.7.0_21-b11)
Java HotSpot(TM) 64-Bit Server VM (build 23.21-b01, mixed mode)

测试案例

这个案例中,分配 32g 比 31g 能创建的对象少了 50%
587889429/385481085 ≈ 1.525

35GB小于32GB

这个案例中,48g 才基本达到 31g 的效果

48GB=31GB

本文出自 qbit snap
阅读 1.6k

推荐阅读
qbit snap
用户专栏

开箱即用,拿走不谢。

206 人关注
106 篇文章
专栏主页