基于 JVM 的语言和应用程序汗牛充栋,不仅限于 Java , 还有 Scala , JPython, JRuby。对于 JVM 的调优是每个JVM 应用开发者必需要了解的。

先回顾一下 JVM 的结构

JVM 结构





jvm参数添加java_opts jvm参数 suspend_java


JVM structure


堆内部的分代

  • 年轻代 Young Generation: 一般分为伊甸园 Eden 和幸存区 Survivor(通过分为两个区S0, S1), 新创建的对象放在Eden 区
  • 年老代 Old Generation
  • 永久代 Permanent Generation, Java 8 中改为 MetaSpace


jvm参数添加java_opts jvm参数 suspend_java_02


JVM Heap


JVM 参数调优

JVM 参数既多且杂,如何提纲挈领,避免挂一漏万呢?个人的想法是掌握原理,了解常用的参数就好了,以度量来驱动适用于你的应用程序的参数设置。

  • 1)设计:
    分析你的应用程序要求的吞吐量如何,它对于短暂的停顿是否敏感,从而了解到大约需要多大的内存,从而设置一个比较合理的参数,例如
-Xms2048m
    -Xmx2048m
    -XX:MetaspaceSize=128m
    -XX:MaxMetaspaceSize=256m
    -XX:CompressedClassSpaceSize=256m
    -Xss1m
  • 2) 运行
    在程序运行期间设置如下参数来打印 GC 的运行时信息
-XX:+PrintGCDetails 
-XX:+PrintGCDateStamps 
-XX:+PrintHeapAtGC 
-XX:+PrintTenuringDistribution 
-XX:+PrintGCApplicationStoppedTime 
-XX:+PrintPromotionFailure 
-XX:+UseGCLogFileRotation 
-XX:NumberOfGCLogFiles=10 
-XX:GCLogFileSize=10M 
-Xloggc:./bin/../logs/gc.log
-XX:OnOutOfMemoryError="sh ./bin/oom-hander.sh"
  • 3) 监控
    重点监控如下指标
  • heap 堆内存使用情况
  • non-heap 非堆内存使用情况
  • gc pause 垃圾回收信息
  • oom 内存溢出错误
  • stackOverFlow 栈溢出错
  • 4)调优
    调整参数,以期更合理的内存分配和垃圾回收

常用JVM 调优参数


jvm参数添加java_opts jvm参数 suspend_java_03


  • JVM内存与垃圾收集
  • GC分类:
  • Minor GC
  • 也称 Scavenge GC, 收集新生代
  • 新对象在 Eden区申请空间失败时会触发
  • Major GC
  • 也称 Full GC, 收集新生代,年老代和永久代
  • 年老代或永久代被写满时会触发
  • GC算法:
  • 引用计数 Reference Counting
  • 标记清除 Mark-Sweep
  • 复制 Copying
  • 标记-压缩 Mark-Compact
  • JVM参数:
  • -Xmx=2048m 堆的最大值
  • -Xms=1024m 堆的初始值
  • -Xmn=128m 年轻代大小的值
  • -XX:newSize= 年轻代大小的初始值
  • -XX:maxNewSize= 年轻代大小的最大值
  • -XX:NewRatio= 年轻代与年老代的比例=年老代大小/年轻代大小
  • -XX:SurvivorRatio= 年轻代中的 Eden 区与 Survivor 区的比例=Eden区大小/(S1或S2区的大小)
  • -XX:MetaSpaceSize= 元空间(永久代)大小的初始值
  • -XX:MaxMetaSpaceSize= 元空间(永久代)大小的最大值
  • -Xss: 每个线程的堆栈大小
  • -XX:MaxDirectMemorySize= 直接内存大小的最大值
  • -XX:CompressedClassSpaceSize= 堆在小于32G时会将类信息放入 CompressedClassSpace, 即地址将64bit压缩成32bit
  • -XX:+AlwaysPreTouch 启动时预先初始化内存页,这样运行期就省去了初始化内存页这个步骤了
  • 指标
  • Throughput 吞吐量
  • the percentage of total time not spent in GC considered over long periods of time
  • Pauses 暂时时间
  • the times when an application appears unresponsive because GC is occurring
  • 分代 Generations 及适应的参数和回收算法
  • 永久代(元数据空间) MetaSpace
  • -XX:MetaspaceSize=128m
  • the initial amount of space(the initial high-water-mark)
  • -XX:MaxMetaspaceSize=256m
  • the maximum amount of space to be allocated for class metadata
  • -XX:MinMetaspaceFreeRatio=
  • min high-water-mark
  • -XX:MaxMetaspaceFreeRatio=
  • max high-water-mark
  • 年轻代 Young Generation
  • Serial
  • -XX:+UseSerialGC
  • ParNew
  • -XX:+UseParNew
  • Parallel Scavenge
  • -XX:+UseParallelGC
  • -XX:ParallelGCThreads=
  • 年老代 Old Generation
  • Serial Old
  • Parallel Old
  • CMS(Concurrent Mark Sweep)
  • -XX:+UseConcMarkSweepGC
  • G1(Gargage First)
  • -XX:+UseG1GC
  • -XX:MaxGCPaouseMillis=
  • -XX:ParallelGChreads=
  • Behavior-BasedTuning
  • 暂停时间目标 Pause Time Goal
  • -XX:MaxGCPauseMillis=
  • 吞吐量目标Throughput Goal
  • -XX:GCTimeRatio=
  • -XX:GCTimeRatio=19 sets a goal of 1/20th or 5% of the total time for GC
  • 体积目标 Footprint Goal
  • reduces the size of the heap until one of the goals(invariably the throughput goal) cannot be met
  • 调优策略 Tuning Strategy
  • 选择适合你的应用程序的吞吐量目标.
  • 选择适合的暂停时间目标,这点要和上面的吞吐量目标可能有冲突,要根据你的需要做出平衡

实例

以 Cassandra 为例,它是的一个高性能的分布式NOSQL 数据存储系统,它设置了如下 JVM 参数:

bin/java -Xloggc:./bin/../logs/gc.log 
-ea 
-XX:+UseThreadPriorities 
-XX:ThreadPriorityPolicy=42 
-XX:+HeapDumpOnOutOfMemoryError 
-Xss256k 
-XX:StringTableSize=1000003 
-XX:+AlwaysPreTouch 
-XX:-UseBiasedLocking 
-XX:+UseTLAB 
-XX:+ResizeTLAB 
-XX:+UseNUMA 
-XX:+PerfDisableSharedMem 
-Djava.net.preferIPv4Stack=true 
-XX:+UseParNewGC 
-XX:+UseConcMarkSweepGC 
-XX:+CMSParallelRemarkEnabled 
-XX:SurvivorRatio=8 
-XX:MaxTenuringThreshold=1 
-XX:CMSInitiatingOccupancyFraction=75 
-XX:+UseCMSInitiatingOccupancyOnly 
-XX:CMSWaitDuration=10000 
-XX:+CMSParallelInitialMarkEnabled 
-XX:+CMSEdenChunksRecordAlways 
-XX:+CMSClassUnloadingEnabled 
-XX:+PrintGCDetails 
-XX:+PrintGCDateStamps 
-XX:+PrintHeapAtGC 
-XX:+PrintTenuringDistribution 
-XX:+PrintGCApplicationStoppedTime 
-XX:+PrintPromotionFailure -XX:+UseGCLogFileRotation 
-XX:NumberOfGCLogFiles=10 
-XX:GCLogFileSize=10M 
-Xms4096M 
-Xmx4096M 
-Xmn800M 
-XX:+UseCondCardMark 
-XX:CompileCommandFile=./bin/../conf/hotspot_compiler 
-javaagent:./bin/../lib/jamm-0.3.0.jar 
-Dcassandra.jmx.local.port=7199 
-Dcom.sun.management.jmxremote.authenticate=false 
-Dcom.sun.management.jmxremote.password.file=/etc/cassandra/jmxremote.password 
-Djava.library.path=./bin/../lib/sigar-bin 
-XX:OnOutOfMemoryError=kill -9 %p 
-Dlogback.configurationFile=logback.xml 
-Dcassandra.logdir=./bin/../logs 
-Dcassandra.storagedir=./bin/../data 
-Dcassandra-foreground=yes 
-cp ./bin/../conf:./bin/../build/classes/main:./bin/../build/classes/thrift:...jar: org.apache.cassandra.service.CassandraDaemon

监控与观察

一般来说,比较常用的方法是通过 JMX 和 GC log 来度量你的 JVM 参数设置是是否合理,一旦发现异常或者 OOM 要马上采取措施进行调整

jstat -gcutil 1324
  S0     S1     E      O      M     CCS    YGC     YGCT    FGC    FGCT     GCT
  0.00   0.00  15.10   7.84  97.24  92.57      2       0.017     1      0.034    0.051
  • S0: Survivor space 0 生存空间0利用率占该空间当前容量的百分比
  • S1: Survivor space 1 幸存者空间1利用率占该空间当前容量的百分比
  • E: Eden space 伊甸园空间利用率占空间当前容量的百分比
  • O: Old space 旧空间利用率占空间当前容量的百分比
  • M: Metaspace 元空间利用率占空间当前容量的百分比
  • CCS: Compressed class space 压缩的类空间利用率(以百分比表示)
  • YGC: Young generation GC 年轻代GC次数.
  • YGCT: Young generation GC Time年轻代GC耗时
  • FGC: Full GC 完全GC次数
  • FGCT: Full GC Time 完全GC耗时
  • GCT: Total GC Time 总GC耗时

关于 JVM 内存溢出的分析可以参考以前写的 内存溢出不可怕,手足无措才尴尬

参考资料