一、jvm调优配置
1.jvm常见配置
-XX:MetaspaceSize=128m (元空间默认大小)
-XX:MaxMetaspaceSize=128m (元空间最大大小)
-Xms1024m (堆默认大小)
-Xmx1024m (堆最大大小)
-Xmn256m (新生代大小)
-Xss256k (棧最大深度大小)
-XX:SurvivorRatio=8 (新生代分区比例 8:2)
-XX:+UseConcMarkSweepGC (指定使用的垃圾收集器,这里使用CMS收集器)
-XX:+PrintGCDetails (打印详细的GC日志)
堆内存调整参数如图所示:
我们可以发现每一个区域都有一个可变的伸缩区,当我们的内存空间不足的时候,会在可变的范围内扩大内存空间,当我们的内存空间变得不紧张的时候我们再释放可变空间。
在堆内存的调优之中我们要特别注意两个参数-Xms初始化内存分配大小,默认为物理内存的1/64,-Xmx 最大的分配内存默认为物理内存的1/4。
查看jvm配置可使用下面的命令
ps -ef | grep java
2.JVM运行情况预估
用 jstat gc -pid 命令可以计算出如下一些关键数据,有了这些数据就可以采用之前介绍过的优化思路,先给自己的系统设置一些初始性的JVM参数,比如堆内存大小,年轻代大小,Eden和Survivor的比例,老年代的大小,大对象的阈值,大龄对象进入老年代的阈值等。
年轻代对象增长的速率
可以执行命令 jstat -gc pid 1000 10 (每隔1秒执行1次命令,共执行10次),通过观察EU(eden区的使用)来估算每秒eden大概新增多少对象,如果系统负载不高,可以把频率1秒换成1分钟,甚至10分钟来观察整体情况。注意,一般系统可能有高峰期和日常期,所以需要在不同的时间分别估算不同情况下对象增长速率。
Young GC的触发频率和每次耗时
知道年轻代对象增长速率我们就能推根据eden区的大小推算出Young GC大概多久触发一次,Young GC的平均耗时可以通过 YGCT/YGC 公式算出,根据结果我们大概就能知道系统大概多久会因为Young GC的执行而卡顿多久。
每次Young GC后有多少对象存活和进入老年代
这个因为之前已经大概知道Young GC的频率,假设是每5分钟一次,那么可以执行命令 jstat -gc pid 300000 10 ,观察每次结果eden,survivor和老年代使用的变化情况,在每次gc后eden区使用一般会大幅减少,survivor和老年代都有可能增长,这些增长的对象就是每次Young GC后存活的对象,同时还可以看出每次Young GC后进去老年代大概多少对象,从而可以推算出老年代对象增长速率。
Full GC的触发频率和每次耗时
知道了老年代对象的增长速率就可以推算出Full GC的触发频率了,Full GC的每次耗时可以用公式 FGCT/FGC 计算得出。
优化思路其实简单来说就是尽量让每次Young GC后的存活对象小于Survivor区域的50%,都留存在年轻代里。尽量别让对象进入老年代。尽量减少Full GC的频率,避免频繁Full GC对JVM性能的影响。
JVM垃圾回收参数实例解析:
root 8508 1 0 11:51 ? 00:02:08 /bin/java -Djava.awt.headless=true -Djava.net.preferIPv4Stack=true -server -Xmx2g -Xms2g -Xmn512m -XX:PermSize=128m -Xss256k -XX:+DisableExplicitGC -XX:+UseConcMarkSweepGC -XX:+CMSParallelRemarkEnabled -XX:+UseCMSCompactAtFullCollection -XX:LargePageSizeInBytes=128m -XX:+UseFastAccessorMethods -XX:+UseCMSInitiatingOccupancyOnly -XX:CMSInitiatingOccupancyFraction=70
-server:
启用-server时新生代默认采用并行收集,其他情况下,默认不启用。
-XX:+UseAdaptiveSizePolicy:
上文中,因启用-server模式,所以新生代使用并行收集器。
设置此选项后,并行收集器会自动选择年轻代区大小和相应的Survivor区比例,以达到目标系统规定的最低相应时间或者收集频率等,建议使用并行收集器时一直打开。
-XX:+DisableExplicitGC
关闭System.gc()
-XX:+UseConcMarkSweepGC:
设置老年代为并发收集。
-XX:+CMSParallelRemarkEnabled
降低标记停顿
-XX:+UseCMSCompactAtFullCollection
在FULL GC的时候, 对老年代的压缩。
CMS是不会移动内存的, 因此, 这个非常容易产生碎片, 导致内存不够用, 因此, 内存的压缩这个时候就会被启用。 增加这个参数是个好习惯。
可能会影响性能,但是可以消除碎片
-XX:LargePageSizeInBytes
内存页的大小不可设置过大, 会影响Perm的大小
-XX:+UseFastAccessorMethods
原始类型的快速优化
XX:+UseCMSInitiatingOccupancyOnly
使用手动定义初始化定义开始CMS收集,目的:禁止hostspot自行触发CMS GC
-XX:CMSInitiatingOccupancyFraction=70
使用cms作为垃圾回收,使用70%后开始CMS收集
其他相关参数:
-XX:PermSize
设置持久代(perm gen)初始值 默认为:物理内存的1/64
-XX:MaxPermSize
设置持久代最大值 默认为:物理内存的1/4
-XX:MaxTenuringThreshold
垃圾最大年龄.如果设置为0的话,则年轻代对象不经过Survivor区,直接进入年老代. 对于年老代比较多的应用,可以提高效率.如果将此值设置为一个较大值,则年轻代对象会在Survivor区进行多次复制,这样可以增加对象再年轻代的存活 时间,增加在年轻代即被回收的概率
该参数只有在串行GC时才有效.
@see 深入了解JVW https://www.cnblogs.com/yang-hao/p/5936059.html
idea 设置jvm运行参数
-Xms100m -Xmx100m
二、jvm调优常用命令
这里主要介绍如下几个工具:
1、jps:查看本机java进程信息
2、jstack:打印线程的栈信息,制作 线程dump文件
3、jmap:打印内存映射信息,制作 堆dump文件
4、jstat:性能监控工具
5、jhat:内存分析工具,用于解析堆dump文件并以适合人阅读的方式展示出来
6、jconsole:简易的JVM可视化工具
7、jvisualvm:功能更强大的JVM可视化工具
JAVA Dump:
JAVA Dump就是虚拟机运行时的快照,将虚拟机运行时的状态和信息保存到文件中,包括:
线程dump:包含所有线程的运行状态,纯文本格式
堆dump:包含所有堆对象的状态,二进制格式
1.jps
显示当前所有java进程pid的命令,我们可以通过这个命令来查看到底启动了几个java进程(因为每一个java程序都会独占一个java虚拟机实例),不过jps有个缺点是只能显示当前用户的进程id,要显示其他用户的还只能用linux的ps命令。
执行jps命令,会列出所有正在运行的java进程,其中jps命令也是一个java程序。前面的数字就是进程的id,这个id的作用非常大,后面会有相关介绍。
jps -v 输出传递给JVM的参数
2.jstack
主要用于生成指定进程当前时刻的线程快照,线程快照是当前java虚拟机每一条线程正在执行的方法堆栈的集合,生成线程快照的主要目的是用于定位线程出现长时间停顿的原因,如线程间死锁、死循环、请求外部资源导致长时间等待。
3.jmap
主要用于打印指定java进程的共享对象内存映射或堆内存细节。
堆Dump是反映堆使用情况的内存镜像,其中主要包括系统信息、虚拟机属性、完整的线程Dump、所有类和对象的状态等。一般在内存不足,GC异常等情况下,我们会去怀疑内存泄漏,这个时候就会去打印堆Dump。
1. jmap -heap pid:查看堆使用情况
jmap 踩坑记录
Windows下jmap命令报错问题 https://www.cnblogs.com/ocean234/p/11525721.html
2. jmap -histo pid:查看堆中对象数量和大小
4.jstat
主要是对java应用程序的资源和性能进行实时的命令行监控,包括了对heap size和垃圾回收状况的监控。
jstat - [-t] [-h] [ []]
option:我们经常使用的选项有gc、gcutil
vmid:java进程id
interval:间隔时间,单位为毫秒
count:打印次数
jstat -gc PID 5000 20
S0C:年轻代第一个survivor的容量(KB)
S1C:年轻代第二个survivor的容量(KB)
S0U:年轻代第一个survivor已使用的容量(KB)
S1U:年轻代第二个survivor已使用的容量(KB)
EC:年轻代中Eden的空间(KB)
EU:年代代中Eden已使用的空间(KB)
OC:老年代的容量(KB)
OU:老年代中已使用的空间(KB)
MC:当前metaspace的容量(KB)
MU:当前metaspace已使用的空间(KB)
CCSC:当前compressed class space的容量(KB)
CCSU:当前compressed class space已使用的空间(KB)
YGC:从应用程序启动到采样时年轻代中GC的次数
YGCT:从应用程序启动到采样时年轻代中GC所使用的时间(单位:S)
FGC:从应用程序启动到采样时老年代中GC(FULL GC)的次数
FGCT:从应用程序启动到采样时老年代中GC所使用的时间(单位:S)
CGC:从应用程序启动到采样时发生concurrent GC的次数
CGCT:从应用程序启动到采样时concurrent GC所用的时间(秒)
GCT:从应用程序启动到采样时垃圾回收所用的总时间(秒)
5.jinfo
jinfo可以用来查看正在运行的java运用程序的扩展参数,甚至支持在运行时动态地更改部分参数。
基本使用语法如下: jinfo -< option > < pid > ,其中option可以为以下信息:
查看当前的应用java参数配置
jinfo -flags pid
6.jcmd
在JDK 1.7之后,新增了一个命令行工具jcmd。它是一个多功能工具,可以用来导出堆,查看java进程,导出线程信息,执行GC等。jcmd拥有jmap的大部分功能,Oracle官方建议使用jcmd代替jmap。
子命令含义:
VM.native_memory
VM.commercial_features
GC.rotate_log
ManagementAgent.stop
ManagementAgent.start_local
ManagementAgent.start
Thread.print, 打印线程栈信息
GC.class_histogram, 查看系统中类统计信息
GC.heap_dump, 导出堆信息,与jmap -dump功能一样
GC.run_finalization, 触发finalize()
GC.run, 触发gc()
VM.uptime, VM启动时间
VM.flags, 获取JVM启动参数
VM.system_properties, 获取系统Properties
VM.command_line, 启动时命令行指定的参数
VM.version
help
如查看JVM启动参数
思考
1.如何用jstack找出占用cpu最高的线程堆栈信息?
我们使用jdk自带的jstack来分析。
思路:
先找到高负载的进程ID,然后查看进程中高负载的线程ID。最后使用jstack 线程ID找到详细的堆栈位置
当linux出现cpu被java程序消耗过高时,以下过程说不定可以帮上你的忙:
1、执行:top
查看高负载的进程
2、top -H -p 28973
查看高负载进程下的高负载线程
把线程号 28973 进行换算成16进制编号:print"%x\n" 28973 ->72d6
3、jstack 28973 >> log.txt
将线程堆栈信息保存为txt文档
4、打开log.txt,find一下72d6
导出进程的堆栈日志,找到72d6 这个线程号
@see jstack命令查看占用CPU高的线程堆栈信息https://www.cnblogs.com/kaymi/p/12667708.html
2.如何查看内存占用最高的对象
通过jconsole分析dump文件。
3.如何查看进程是否频繁进行fullGC并解决
使用的jvm参数为
-Xms100m -Xmx100m -Xmn30m
代码地址见:
1.使用jstat命令分析是否频繁触发fullGC
可见频繁的进行了full gc
2.使用jmap -histo 1358 查看是否存在大对象
user对象创建了500个实例,每个实例0.1M,并放在了一个list集合中,将是50M,大于新生代的大小30M,将放在老年代中。长次以往将不断的触发full gc.
解决办法:
调大新生代的大小,让list对象通过Minor gc进行回收掉。
4.发生OOM如何排查?
1.首先增加两个参数便于,当 OOM 发生时自动 dump 堆内存信息
到指定目录。
-XX:+HeapDumpOnOutOfMemoryError
-XX:HeapDumpPath=/tmp/heapdump.hprof
2.使用 MAT 等工具载入到 dump 文件, 分析大对象的占用情况, 比如 HashMap 做缓存未清理, 时间长了就会内存溢出, 可以把改为弱引用。
jvm调优工具使用
1.VisualVM
参考资料
1.JDK工具(查看JVM参数、内存使用情况及分析等)
https://www.cnblogs.com/z-sm/p/6745375.html