基于 JVM 的语言和应用程序汗牛充栋,不仅限于 Java , 还有 Scala , JPython, JRuby。对于 JVM 的调优是每个JVM 应用开发者必需要了解的。
先回顾一下 JVM 的结构
JVM 结构
JVM structure堆内部的分代
- 年轻代 Young Generation: 一般分为伊甸园 Eden 和幸存区 Survivor(通过分为两个区S0, S1), 新创建的对象放在Eden 区
- 年老代 Old Generation
- 永久代 Permanent Generation, Java 8 中改为 MetaSpace
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内存与垃圾收集
- GC分类:
- Minor GC
- 也称 Scavenge GC, 收集新生代
- 新对象在 Eden区申请空间失败时会触发
- Major GC
- 也称 Full GC, 收集新生代,年老代和永久代
- 年老代或永久代被写满时会触发
- Minor 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
- Throughput 吞吐量
- 分代 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
- -XX:MetaspaceSize=128m
- 年轻代 Young Generation
- Serial
- -XX:+UseSerialGC
- ParNew
- -XX:+UseParNew
- Parallel Scavenge
- -XX:+UseParallelGC
- -XX:ParallelGCThreads=
- Serial
- 年老代 Old Generation
- Serial Old
- Parallel Old
- CMS(Concurrent Mark Sweep)
- -XX:+UseConcMarkSweepGC
- G1(Gargage First)
- -XX:+UseG1GC
- -XX:MaxGCPaouseMillis=
- -XX:ParallelGChreads=
- 永久代(元数据空间) MetaSpace
- 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
- -XX:GCTimeRatio=
- 体积目标 Footprint Goal
- reduces the size of the heap until one of the goals(invariably the throughput goal) cannot be met
- 暂停时间目标 Pause Time Goal
- 调优策略 Tuning Strategy
- 选择适合你的应用程序的吞吐量目标.
- 选择适合的暂停时间目标,这点要和上面的吞吐量目标可能有冲突,要根据你的需要做出平衡
- GC分类:
实例
以 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 内存溢出的分析可以参考以前写的 内存溢出不可怕,手足无措才尴尬
参考资料
- Book: Inside the Java Virtual Machine by Bill Venners
- JVM options: https://www.oracle.com/java/technologies/javase/vmoptions-jsp.html
网友评论