JVM性能监控与调优
2023-05-28 19:42:30 1 举报
AI智能生成
基于尚硅谷宋红康老师及图灵JVM视频总结
作者其他创作
大纲/内容
概述
大厂面试题
背景说明
1、生产环境中的问题
生产环境应该给服务器分配多少内存合适?
如何对垃圾回收器的性能进行调优?
生产环境应该给应用分配多少线程合适?
不加log,如何确定请求是否执行了某一行代码?
子主题
2、为什么要调优
防止出现OOM
解决OOM
减少Full GC出现的频率
3、不同阶段的考虑
上线前
项目运行阶段
线上出现OOM
调优概述
1、监控的依据
运行日志
异常堆栈
GC日志
线程快照
堆转储快照
2、调优的大方向
合理地编写代码
充分并合理的使用硬件资源
合理地进行JVM调优
性能优化的步骤
第1步(发现问题):性能监控
GC频繁
cpu load过高
OOM
内存泄露
死锁
程序响应时间较长
第2步(排查问题):性能分析
打印GC日志,通过GCviewer或者 http://gceasy.io来分析异常信息
灵活运用命令行工具、jstack、jmap、jinfo等
dump出堆文件,使用内存分析工具分析文件
使用阿里Arthas、jconsole、JVisualVM来实时查看JVM状态
jstack查看堆栈信息
第3步(解决问题):性能调优
适当增加内存,根据业务背景选择垃圾回收器
优化代码,控制内存使用
增加机器,分散节点压力
合理设置线程池线程数量
使用中间件提高程序效率,比如缓存、消息队列等
性能评价/测试指标
停顿时间(或响应时间)
在垃圾回收器环节中,执行垃圾收集时,程序的工作线程被暂停的时间(STW)
吞吐量
对单位时间内完成的工作量(请求)的量度
在GC中:运行用户代码的事件占总运行时间的比例(总运行时间:程序的运行时间+内存回收的时间)
吞吐量为1-1/(1+n),其中-XX::GCTimeRatio=n
吞吐量为1-1/(1+n),其中-XX::GCTimeRatio=n
并发数
同一时刻,对服务器有实际交互的请求数
内存占用
Java堆区所占的内存大小
相互间的关系
以高速公路通行状况为例
JVM监控及诊断工具-命令行
概述
简单命令行工具
源码
jps: 查询正在运行的Java进程
jps(Java Process Status):显示指定系统内所有的HotSpot虚拟机进程(查看虚拟机进程信息),可用于查询正在运行的虚拟机进程(对于本地虚拟机进程来说,进程的本地虚拟机ID与操作系统的进程ID是一致的,是唯一的)
语法
options 参数
-q :仅仅显示LVMID (local virtual machine id),即本地虚拟机唯一id。不显示主类的名称等
-l :输出应用程序主类的全类名 或 如果进程执行的是jar包,则输出jar完整路径
-m :输出虚拟机进程启动时传递给主类main()的参数
-v :列出虚拟机进程启动时的JVM参数。 比如:-Xms20m -Xmx50m是启动程序指定的jvm参数。
说明:以上参数可以综合使用。
补充:如果某Java进程关闭了默认开启的UsePerfData参数(即使用参数-XX:-UsePerfData) ,那么jps命令(以及下面介绍的jstat)将无法探知该Java 进程。
补充:如果某Java进程关闭了默认开启的UsePerfData参数(即使用参数-XX:-UsePerfData) ,那么jps命令(以及下面介绍的jstat)将无法探知该Java 进程。
如何将信息输出到同级文件中
语法:命令 > 文件名称
例如:jps -l > a.txt
例如:jps -l > a.txt
hostid参数
RMI注册表中注册的主机名。如果想要远程监控主机上的 java 程序,需要安装 jstatd。
对于具有更严格的安全实践的网络场所而言,可能使用一个自定义的策略文件来显示对特定的可信主机或网络的访问,尽管这种技术容易受到IP地址欺诈攻击。
如果安全问题无法使用一个定制的策略文件来处理,那么最安全的操作是不运行jstatd服务器,而是在本地使用jstat和jps工具。
对于具有更严格的安全实践的网络场所而言,可能使用一个自定义的策略文件来显示对特定的可信主机或网络的访问,尽管这种技术容易受到IP地址欺诈攻击。
如果安全问题无法使用一个定制的策略文件来处理,那么最安全的操作是不运行jstatd服务器,而是在本地使用jstat和jps工具。
jstat:查看JVM统计信息
jstat(JVM Statistics Monitoring Tool):用于监视虚拟机各种运行状态信息的命令行工具。它可以显示本地或者远程虚拟机进程中的类装载、内存、垃圾收集、JIT编译等运行数据。 在没有GUI图形界面,只提供了纯文本控制台环境的服务器上,它将是运行期定位虚拟机性能问题的首选工具。常用于检测垃圾回收问题以及内存泄漏问题。
语法
option参数
-class
- Timestamp代表程序至今的运行时间,单位为秒;
- Loaded代表加载的类的数目;
- Bytes代表加载的类的总字节数;
- Unloaded代表卸载的类的数目;
- Time代表类装载所消耗的时间;
-gc
- S0C代表幸存者0区的总容量,
- S1C代表幸存者1区的总容量,
- S0U代表幸存者0区使用的容量,
- S1U代表幸存者1区使用的容量,
- EC代表伊甸园区的总容量,
- EU代表伊甸园区使用的总容量,
- OC代表老年代的总容量,
- OU代表老年代已经使用的容量,
- MC代表方法区的总容量,
- MU代表方法区的总容量,
- CCSC代表压缩类的总容量,
- CCSU代表压缩类使用的容量,
- YGC代表年轻代垃圾回收的次数,
- YGCT年轻代进行垃圾回收需要的时间,
- FGC代表代表Full GC的次数,
- FGCT代表Full GC的时间,
- GCT代表垃圾回收的总时间
新生代相关
S0C是第一个幸存者区的大小(字节)
S1C是第二个幸存者区的大小(字节)
S0U是第一个幸存者区已使用的大小(字节)
S1U是第二个幸存者区已使用的大小(字节)
EC是Eden空间的大小(字节)
EU是Eden空间已使用大小(字节)
老年代相关
OC是老年代的大小(字节)
OU是老年代已使用的大小(字节)
方法区(元空间)相关
MC是方法区的大小
MU是方法区已使用的大小
CCSC是压缩类空间的大小
CCSU是压缩类空间已使用的大小
其他
YGC是从应用程序启动到采样时young
YGCT是指从应用程序启动到采样时young gc消耗时间(秒)
FGC是从应用程序启动到采样时full gc的次数
FGCT是从应用程序启动到采样时的full gc的消耗时间(秒)
GCT是从应用程序启动到采样时gc的总时间
-gccapacity : 【堆内存统计】
- NGCMN:新生代最小容量
- NGCMX:新生代最大容量
- NGC:当前新生代容量
- S0C:第一个幸存区大小
- S1C:第二个幸存区的大小
- EC:伊甸园区的大小
- OGCMN:老年代最小容量
- OGCMX:老年代最大容量
- OGC:当前老年代大小
- OC:当前老年代大小
- MCMN:最小元数据容量
- MCMX:最大元数据容量
- MC:当前元数据空间大小
- CCSMN:最小压缩类空间大小
- CCSMX:最大压缩类空间大小
- CCSC:当前压缩类空间大小
- YGC:年轻代gc次数
- FGC:老年代GC次数
-gcutil
- S0:幸存1区当前使用比例
- S1:幸存2区当前使用比例
- E:伊甸园区使用比例
- O:老年代使用比例
- M:元数据区使用比例
- CCS:压缩使用比例
- YGC:年轻代垃圾回收次数
- FGC:老年代垃圾回收次数
- FGCT:老年代垃圾回收消耗时间
- GCT:垃圾回收消耗总时间
-gccause 【垃圾回收情况】
- S0代表幸存者0区,
- S1代表幸存者1区,
- E代表伊甸园区,
- O代表老年代,
- M代表方法区,
- CCS代表压缩类,以上这些值都是占比情况,
- YGC代表年轻代垃圾回收的次数,
- YGCT年轻代进行垃圾回收需要的时间,
- FGC代表代表Full GC的次数,
- FGCT代表Full GC的时间,
- GCT代表垃圾回收的总时间,
- LGCC和GCC代表垃圾回收的原因
-gcnew 【新生代垃圾回收】
- S0C:第一个幸存区的大小
- S1C:第二个幸存区的大小
- S0U:第一个幸存区的使用大小
- S1U:第二个幸存区的使用大小
- TT:对象在新生代存活的次数
- MTT:对象在新生代存活的最大次数
- DSS:期望的幸存区大小
- EC:伊甸园区的大小
- EU:伊甸园区的使用大小
- YGC:年轻代垃圾回收次数
- YGCT:年轻代垃圾回收消耗时间
-gcnewcapacity 【新生代空间统计】
- NGCMN:新生代最小容量
- NGCMX:新生代最大容量
- NGC:当前新生代容量
- S0CMX:最大幸存1区大小
- S0C:当前幸存1区大小
- S1CMX:最大幸存2区大小
- S1C:当前幸存2区大小
- ECMX:最大伊甸园区大小
- EC:当前伊甸园区大小
- YGC:年轻代垃圾回收次数
- FGC:老年代回收次数
-gcold 【老年代垃圾回收统计】
- MC:方法区大小
- MU:方法区使用大小
- CCSC:压缩类空间大小
- CCSU:压缩类空间使用大小
- OC:老年代大小
- OU:老年代使用大小
- YGC:年轻代垃圾回收次数
- FGC:老年代垃圾回收次数
- FGCT:老年代垃圾回收消耗时间
- GCT:垃圾回收消耗总时间
-gcoldcapacity 【老年代空间统计】
- OGCMN:老年代最小容量
- OGCMX:老年代最大容量
- OGC:当前老年代大小
- OC:老年代大小
- YGC:年轻代垃圾回收次数
- FGC:老年代垃圾回收次数
- FGCT:老年代垃圾回收消耗时间
- GCT:垃圾回收消耗总时间
-gcmatecapacity 【元数据空间统计】
- MCMN:最小元数据容量
- MCMX:最大元数据容量
- MC:当前元数据空间大小
- CCSMN:最小压缩类空间大小
- CCSMX:最大压缩类空间大小
- CCSC:当前压缩类空间大小
- YGC:年轻代垃圾回收次数
- FGC:老年代垃圾回收次数
- FGCT:老年代垃圾回收消耗时间
- GCT:垃圾回收消耗总时间
-compiler 【JIT编译过的方法,耗时等】
-printcompilation 【输出以及被JIT编译的方法】
interval参数
用于指定输出统计数据的周期,单位为毫秒。即:查询间隔
count参数
用于指定查询的总次数
-t参数
可以在输出信息前加上一个Timestamp列,显示程序的运行时间。单位:秒
经验
我们可以比较Java进程的启动时间以及总GC时间(GCT列) ,或者两次测量的间隔时间以及总GC时间的增量,来得出GC时间占运行时间的比例。如果该比例超过20%,则说明目前堆的压力较大;如果该比例超过90%,则说明堆里几乎没有可用空间,随时都可能抛出 OOM 异常。
我们执行jstat -gc -t 13152 1000 10,这代表1秒打印出1行,一共10行,-t代表打印出Timestamp总运行时间,结果如下所示:
上方红色框框中代表Timestamp,而蓝色框框中代表垃圾回收时间,单位都是秒,如果让红色框框中的某两个值相减,假设这个值是num1,然后让对应行的蓝色框框中的另外两个值相减,假设这个值是num2,之后让num2/num1,得出的差值就是上述所说的GC时间占运行时间的比例
虽然这种方式比较繁琐,但是在项目部署之后就需要使用命令行去看了,就没有可视化界面了,所以这种方式也要会
虽然这种方式比较繁琐,但是在项目部署之后就需要使用命令行去看了,就没有可视化界面了,所以这种方式也要会
-h参数
可以在周期性数据输出时,输出多少行数据后输出一个表头信息
jinfo:实时查看和修改JVM配置参数
jinfo (Configuration Inof for Java) 查看虚拟机配置参数信息,也可以动态调整虚拟机的配置参数
语法
查看
jinfo -sysprops 进程id
可以查看由System.getProperties()取得的参数
jinfo -flags 进程id
查看我们自己设置或者默认的参数信息
jinfo -flag 参数名称 进程id
查看某个java进程的具体参数信息
修改
jinfo不仅可以查看运行时某一个Java虚拟机参数的实际取值,甚至可以在运行时修改部分参数,并使之立即生效。但是,并非所有参数都支持动态修改。参数只有被标记为manageable的flag可以被实时修改。其实,这个修改能力是极其有限的
查看被标记为 manageable的参数:
java -XX:printFlagsFinal -version
java -XX:printFlagsFinal -version
针对boolean类型
jinfo -flag [+|-]参数名称 进程id
针对非boolean类型
jinfo -flag 参数名称=参数值 进程id
java -XX:+PrintFlagsInitial
查看所有JVM参数启动的初始值
java -XX:+PrintFlagsFinal
查看所有JVM参数的最终值
java -参数名称:+PrintCommandLineFlags
查看那些已经被用户或者JVM设置过的详细的XX参数的名称和值
jmap:导出内存映像文件&内存使用情况
jmap(JVM Memory Map):作用一方面是获取dump文件(堆转储快照文件,二进制文件),它还可以获取目标Java进程的内存相关信息括Java堆各区域的使用情况、堆中对象的统计信息、类加载信息等。
语法
-dump
生成Java堆转储快照:dump文件
特别的:-dump:live只保存堆中的存活对象
-heap
输出整个堆空间的详细信息,包括GC的使用、堆配置信息,以及内存的使用信息等
-histo
输出堆中对象的同级信息,包括类、实例数量和合计容量
特别的:-histo:live只统计堆中的存活对象
-permstat
以ClassLoader为统计口径输出永久代的内存状态信息
仅linux/solaris平台有效
-finalizerinfo
显示在F-Queue中等待Finalizer线程执行finalize方法的对象
仅linux/solaris平台有效
-F
当虚拟机进程对-dump选项没有任何响应时,可使用此选项强制执行生成dump文件
仅linux/solaris平台有效
C-h | -help
jamp工具使用的帮助命令
-J <flag>
传递参数给jmap启动的jvm
使用
导出内存映像文件(主要作用)
手动的方式
jmap -dump:format=b,file=<filename.hprof> <pid>
jmap -dump:live,format=b,file=<filename.hprof> <pid>
- 由于jmap将访问堆中的所有对象,为了保证在此过程中不被应用线程干扰,jmap需要借助安全点机制,让所有线程停留在不改变堆中数据的状态。也就是说,由jmap导出的堆快照必定是安全点位置的。这可能导致基于该堆快照的分析结果存在偏差。
- 举个例子,假设在编译生成的机器码中,某些对象的生命周期在两个安全点之间,那么:live选项将无法探知到这些对象。
- 另外,如果某个线程长时间无法跑到安全点, jmap将一直等下去。与前面讲的jstat则不同,垃圾回收器会主动将jstat所需要的摘要数据保存至固定位置之中,而jstat只需直接读取即可。
自动的方式
-XX:+HeapDumpOnOutOfMemoryError
程序发生OOM时,导出应用程序当前堆快照
-XX:HeapDumpPath=<filename.hprof>
可以指定堆快照的保存位置
显示堆内存相关信息
jmap -heap 进程id
jmap -histo 进程id
查看系统的ClassLoader信息
jmap -permstat 进程id
查看堆积在finalizer队列中的对象
jmap -finalizerinfo
jhat:JDK自带堆分析工具
jhat(JVM Heap Analysis Tool)
Sun JDK提供的jhat命令与jmap命令搭配使用,用于分析jmap生成的heap dump文件(堆转储快照) 。jhat内置了一个微型的HTTP/HTML服务器, 生成dump文件的分析结果后,可以在浏览器中查看分析结果(分析虚拟机转储快照信息)
jdk8、jdk9 中已经被删除,官方建议用VisualVM参数
语法
jstack:打印JVM中线程快照
jstack(JVM Stack Trace):用于生成虚拟机指定进程当前时刻的线程快照(虚拟机堆栈跟踪)。线程快照就是当前虚拟机内指定进程的每一条线程正在执行的方法堆栈的集合。生成线程快照的作用:可用于定位线程出现长时间停顿的原因,如线程间死锁、死循环、请求外部资源导致的长时间等待等问题。这些都是导致线程长时间停顿的常见原因。当线程出现停顿时,就可以用jstack显示各个线程调用的堆栈情况
- "Thread-1" 线程名
- prio=5 优先级=5
- tid=0x000000001fa9e000 线程id
- nid=0x2d64 线程对应的本地线程标识nid
- java.lang.Thread.State: BLOCKED 线程状态
在thread dump 中要留意下面几种状态:
- 死锁: Deadlock
- 等待资源: Wait on Condition
- 等待获取监视器:Waiting on monitor entry
- 阻塞: Blocked
- 执行中:Runnable
- 暂停: Suspend
- 对象等待中: Object.wati() 或者 TIMED_WAITING
- 停止:Parked
死锁
语法
option参数:-F
当正常输出的请求不被响应时,强制输出线程堆栈
option参数:-l
除堆栈外,显示关于锁的附加信息
option参数:-m
如果调用本地方法的话,可以显示C/C++的堆栈
option参数:-h
帮助操作
jcmd:多功能命令行
它是一个多功能的工具,可以用来实现前面除了jstat之外所有命令的功能。比如:用它来导出堆、内存使用、查看Java进程、导出线程信息、执行GC、JVM运行时间等。
语法
jcmd -l
列出所有的JVM进程
jcmd 进程号 help
针对指定的进程,列出支持的所有具体命令
jcmd 进程号 具体命令
显示指定进程的指令命令的数据
- Thread.print 可以替换 jstack指令
- GC.class_histogram 可以替换 jmap中的-histo操作
- GC.heap_dump 可以替换 jmap中的-dump操作
- GC.run 可以查看GC的执行情况
- VM.uptime 可以查看程序的总执行时间,可以替换jstat指令中的-t操作
- VM.system_properties 可以替换 jinfo -sysprops 进程id
- VM.flags 可以获取JVM的配置参数信息
jstatd:远程主机信息收集
JVM监控及诊断工具-GUI
Jconsole
JDK5开始,JDK自带的Java监控和管理控制台
用于对JVM中内存、线程和类等的监控,是一个基于JMX(java management extensions)的GUI性能监控工具。
用于对JVM中内存、线程和类等的监控,是一个基于JMX(java management extensions)的GUI性能监控工具。
启动
在jdk安装目录中找到jconsole.exe,双击该可执行文件就可以
打开DOS窗口,直接输入jconsole就可以了
三种连接方式
Local
使用JConsole连接一个正在本地系统运行的JVM,并且执行程序的和运行JConsole的需要是同一个用户。JConsole使用文件系统的授权通过RMI连接起链接到平台的MBean的服务器上。这种从本地连接的监控能力只有Sun的JDK具有。
Remote
使用下面的URL通过RMI连接器连接到一个JMX代理,service:jmx:rmi:///jndi/rmi://hostName:portNum/jmxrmi。JConsole为建立连接,需要在环境变量中设置mx.remote.credentials来指定用户名和密码,从而进行授权。
Advanced
使用一个特殊的URL连接JMX代理。一般情况使用自己定制的连接器而不是RMI提供的连接器来连接JMX代理,或者是一个使用JDK1.4的实现了JMX和JMX Rmote的应用
Visual VM
连接方式
本地连接
监控本地Java进程的CPU、类、线程等
远程连接
1-确定远程服务器的ip地址
2-添加JMX(通过JMX技术具体监控远程服务器哪个Java进程)
3-修改bin/catalina.sh文件,连接远程的tomcat
4-在…/conf中添加jmxremote.access和jmxremote.password文件
5-将服务器地址改成公网ip地址
6-设置阿里云安全策略和防火墙策略
7-启动tomcat,查看tomcat启动日志和端口监听
8-JMX中输入端口号、用户名、密码登录
主要功能
1.生成/读取堆内存快照
2.查看JVM参数和系统属性
3.查看运行中的虚拟机进程
4.生成/读取线程快照
5.程序资源的实时监控
6.其他功能
JMX代理连接
远程环境监控
CPU分析和内存分析
Eclipse MAT
MAT(Memory Analyzer Tool)工具是一款功能强大的Java堆内存分析器。可以用于查找内存泄漏以及查看内存消耗情况。MAT是基于Eclipse开发的,不仅可以单独使用,还可以作为插件的形式嵌入在Eclipse中使用。是一款免费的性能分析工具,使用起来非常方便
httns://ww eclipse.ore/mat/downloads php下载使用MAT
httns://ww eclipse.ore/mat/downloads php下载使用MAT
获取推 dump 文件
dump文件内存
MAT可以分析heap dump文件。在进行内存分析时,"只要获得了反映当前设备内存映像的hprof文件,通过MAT打开就可以直观地看到当前的内存信息。
- 所有的对象信息,包括对象实例、成员变量、存储于栈中的基本类型值和存储于堆中的其他对象的引用值。
- 所有的类信息,包括classloader、类名称、父类、静态变量等
- GCRoot到所有的这些对象的引用路径
- 线程信息,包括线程的调用栈及此线程的线程局部变量(TLS)
说明
分析堆 dump 文件
histogram
展示了各个类的实例数目以及这些实例的Shallow heap【浅堆】或者Retained heap【深堆】的总和
thread overview
查看系统中的Java线程
查看局部变量的信息
获得对象互相引用的关系
with outgoing references : 我引用了谁
with incoming references : 谁引用了我
浅堆与深堆
shallow heap(浅堆)
一个对象所消耗的内存
retained heap (深堆)
对象保留集中所有对象的浅堆纸盒
保留集(Retained Set)
保留集(Retained Set): 只有能被回收的对象才会被包含,例如C对象被A、B对象同时引用,C则不包含在A的保留集中。
当前深堆大小 = 当前对象的浅堆大小 + 对象中所包含对象的深堆大小
对象的大小
对象的实际大小 >= 对象的深堆大小
支配树(Dominator Tree)
支配树的概念来自于图论
MAT提供了一个称为支配树(Dominator Tree)的对象图。支配树体现了对象实例间的支配关系。在对象引用图中,所有指向对象B的路径都经过对象A,则认为对象A支配对象B。如果对象A是离对象B最近的一个支配对象,则认为对象A为对象B的直接支配者。支配树是基于对象间的引用图所建立的,它有以下基本性质:
- 对象A的子树(所有被对象A支配的对象集合)表示对象A的保留集(retained set) ,即深堆。
- 如果对象A支配对象B,那么对象A的直接支配者也支配对象B。
- 支配树的边与对象引用图的边不直接对应。
MAT里的支配树
案例:Tomcat堆溢出分析
支持使用OQL语言查询对象信息
SELECT子句
FROM子句
WHERE子句
内置对象与方法
JProfiler
在运行Java的时候有时候想测试运行时占用内存情况,这时候就需要使用测试工具查看了。在eclipse里面有Eclipse Memory Analyzer tool(MAT)插件可以测试,而在IDEA中也有这么一个,插件,就是JProfiler。
特点
主要功能
方法调用
对方法调用的分析可以帮助您了解应用程序正在做什么,并找到提高其性能的方法
内存分配
通过分析堆上对象、引用链和垃圾收集能帮您修复内存泄露问题,优化内存使用
线程和锁
JProfiler提供多种针对线程和锁的分析视图助您发现多线程问题
高级子系统
许多性能问题都发生在更高的语义级别上。例如,对于JDBC调用,您可能希望找出执行最慢的SQL语句。JProfiler支持对这些子系统进行集成分析
具体使用
数据采集方式
- instrumentation重构模式
- Sampling抽样模式
遥感监测 Telemetries
内存视图 Live Memory
堆遍历 heap walker
cpu视图 cpu views
线程视图 threads
监视器&锁 Monitors&locks
Arthas
具体操作查看官方文档即可
Java Misssion Control
历史
启动
概述
功能:实时监控 JVM运行时的状态
Java Flight Recorder
事件类型
启动方式
方式1:使用-XX:StartFlightRecording=参数
方式2:使用jcmd的JFR.*子命令
方式3:JMC的JFR插件
具体使用:
1、启动飞行记录仪
2、启动飞行记录
3、 正式启动
其他工具
JVM运行时参数
JVM参数选择
类型一:标准参数选项
特点
比较稳定,后续版本基本不会变化
以-开头
各种选项
直接在DOS窗口中运行java或者java -help可以看到所有的标准选项
-server & -client
server
64位机器上只支持server模式的JVM,适用于需要大内存的应用程序,默认使用并行垃圾收集器
client
在32位Windows系统上,默认使用Client类型的JVM。要想使用Server模式,则机器配置至少有2个以上的CPU和2G以上的物理内存。 client模式适用于对内存要求较小的桌面应用程序,默认使用Serial串行垃圾收集器
类型二:-X参数选项
特点
非标准化参数
功能还是比较稳定的。但官方说后续版本可能会变更
以-X开头
各种选项
JVM的JIT编译模式相关的选项
-Xint
只使用解释器:所有字节码都被解释执行,这个模式的速度是很慢的
-Xcomp
只使用编译器:所有字节码第一次使用就被编译成本地代码,然后在执行
-Xmixed
混合模式:这是默认模式,刚开始的时候使用解释器慢慢解释执行,后来让JIT即时编译器根据程序运行的情况,有选择地将某些热点代码提前编译并缓存在本地,在执行的时候效率就非常高了
-Xmx -Xms -Xss属于XX参数?
-Xms<size> 设置初始Java堆大小,等价于-XX:InitialHeapSize
-Xmx<size> 设置最大Java堆大小,等价于-XX:MaxHeapSize
-Xss<size> 设置Java线程堆栈大小,等价于-XX:ThreadStackSize
类型三:-XX参数选项
特点
非标准化参数
使用的最多的参数类型
这类选项属于实验性,不稳定
以-XX开头
作用
用于开发和调试JVM
分类
Boolean类型格式
-XX:+<option> 表示启用option属性
-XX:-<option>表示禁用option属性
例子
非Boolean类型格式(key-value类型)
子类型1:数值型格式-XX:<option>=<number>
子类型2:非数值型格式-XX:<name>=<string>
-XX:+PrintFlagsFinal
输出所有参数的名称和默认值
默认不包括Diagnostic和Experimental的参数(可以配合-XX:+UnlockDiagnosticVMOptions和-XX:UnlockExperimentalVMOptions使用)
添加JVM参数选项
Eclipes
IDEA
运行jar包
java -Xms50m -Xmx50m -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -jar demo.jar
通过Tomcat运行war包
Linux系统下可以在tomcat/bin/catalina.sh中添加类似如下配置:
JAVA_OPTS="-Xms512M -Xmx1024M"
JAVA_OPTS="-Xms512M -Xmx1024M"
Windows系统下载catalina.bat中添加类似如下配置:
set "JAVA_OPTS=-Xms512M -Xmx1024M"
set "JAVA_OPTS=-Xms512M -Xmx1024M"
程序运行过程中
使用jinfo -flag <name>=<value> <pid>设置非Boolean类型参数
使用jinfo -flag [+|-]<name> <pid>设置Boolean类型参数
常用的JVM参数选项
打印设置的XX选项及值
-XX:+PrintCommandLineFlags
可以让程序运行前打印出用户手动设置或者JVM自动设置的XX选项
-XX:+PrintFlagsInitial
表示打印出所有XX选项的默认值
-XX:+PrintFlagsFinal
表示打印出XX选项在运行程序时生效的值
-XX:+PrintVMOptions
打印JVM的参数
堆、栈、方法区等内存大小设置
栈
-Xss128k
等价于-XX:ThreadStackSize,设置每个线程的栈大小为128k
堆内存
-Xms3550m
等价于-XX:InitialHeapSize,设置JVM初始堆内存为3500M
-Xmx3550m
等价于-XX:MaxHeapSize,设置JVM最大堆内存为3500M
-Xmn2g
设置年轻代大小为2G,即等价于-XX:NewSize=2g -XX:MaxNewSize=2g,也就是设置年轻代初始值和年轻代最大值都是2G
官方推荐配置为整个堆大小的3/8
-XX:NewSize=1024m
设置年轻代初始值为1024M
-XX:MaxNewSize=1024m
设置年轻代最大值为1024M
-XX:SurvivorRatio=8
设置年轻代中Eden区与一个Survivor区的比值,默认为8
-XX:+UseAdaptiveSizePolicy
自动选择各区大小比例,默认开启
默认开启,将会导致Eden区和Survivor区的比例自动分配,因此也会引起我们默认值-XX:SurvivorRatio=8失效,所以真实比例可能不是8,比如可能是6等
-XX:SurvivorRatio=8
- 显示使用Eden区和Survivor区的比例,那就使用我自己的
- 没有显示使用Eden区和Survivor区的比例,无论打开或者关闭-XX:+UseAdaptiveSizePolicy,都会自动设置Eden区和Survivor区的比例
-XX:NewRatio=2
设置老年代与年轻代(包括1个Eden区和2个Survivor区)的比值,默认为2
-XX:PretenureSizeThreadshold=1024
设置让大于此阈值的对象直接分配在老年代,单位为字节
只对Serial、ParNew收集器有效
-XX:MaxTenuringThreshold=15
新生代每次MinorGC后,还存活的对象年龄+1,当对象的年龄大于设置的这个值时就进入老年代
-XX:+PrintTenuringDistribution
让JVM在每次MinorGC后打印出当前使用的Survivor中对象的年龄分布
-XX:TargetSurvivorRatio
-XX:TargetSurvivorRatio
方法区
永久代
-XX:PermSize=256m
设置永久代初始值为256M
-XX:MaxPermSize=256m
设置永久代最大值为256M
元空间
-XX:MetaspaceSize
初始空间大小
-XX:MaxMetaspaceSize
最大空间,默认没有限制
-XX:+UseCompressedOops
使用压缩对象指针
-XX:+UseCompressedClassPointers
使用压缩类指针
-XX:CompressedClassSpaceSize
设置Klass Metaspace的大小,默认1G
直接内存
-XX:MaxDirectMemorySize
指定DirectMemory容量,若未指定,则默认与Java堆最大值一样
OutOfMemory相关的选项
-XX:+HeapDumpOnOutMemoryError
表示在内存出现OOM的时候,生成Heap转储文件,以便后续分析,-XX:+HeapDumpBeforeFullGC和-XX:+HeapDumpOnOutMemoryError只能设置1个
-XX:+HeapDumpBeforeFullGC
表示在出现FullGC之前,生成Heap转储文件,以便后续分析,-XX:+HeapDumpBeforeFullGC和-XX:+HeapDumpOnOutMemoryError只能设置1个,请注意FullGC可能出现多次,那么dump文件也会生成多个
(程序结束之前也会生成)
(程序结束之前也会生成)
-XX:HeapDumpPath=<path>
指定heap转存文件的存储路径,如果不指定,就会将dump文件放在当前目录中
-XX:OnOutOfMemoryError
指定一个可行性程序或者脚本的路径,当发生OOM的时候,去执行这个脚本
GC日志相关选项
常用参数
-verbose:gc
输出日志信息,默认输出的标准输出
可以独立使用
-XX:+PrintGC
等同于-verbose:gc(表示打开简化的日志)
可以独立使用
-XX:+PrintGCDetails
在发生垃圾回收时打印内存回收详细的日志,并在进程退出时输出当前内存各区域的分配情况
可以独立使用
-XX:+PrintGCTimeStamps
程序启动到GC发生的时间秒数
不可以独立使用,需要配合-XX:+PrintGCDetails使用
-XX:+PrintGCDateStamps
输出GC发生时的时间戳(以日期的形式,例如:2013-05-04T21:53:59.234+0800)
不可以独立使用,可以配合-XX:+PrintGCDetails使用
-XX:+PrintHeapAtGC
每一次GC前和GC后,都打印堆信息
可以独立使用
-XIoggc:<file>
把GC日志写入到一个文件中去,而不是打印到标准输出中
其他参数
-XX:TraceClassLoading
监控类的加载
-XX:PrintGCApplicationStoppedTime
打印GC时线程的停顿时间
-XX:+PrintGCApplicationConcurrentTime
垃圾收集之前打印出应用未中断的执行时间
-XX:+PrintReferenceGC
记录回收了多少种不同引用类型的引用
-XX:+PrintTenuringDistribution
让JVM在每次MinorGC后打印出当前使用的Survivor中对象的年龄分布
-XX:+UseGCLogFileRotation
启用GC日志文件的自动转储
-XX:NumberOfGCLogFiles=1
GC日志文件的循环数目
-XX:GCLogFileSize=1M
控制GC日志文件的大小
实例
java -jar -Xloggc:./gc-%t.log -XX:+PrintGCDetails -XX:+PrintGCDateStamps -XX:+PrintGCTimeStamps -XX:+PrintGCCause
-XX:+UseGCLogFileRotation -XX:NumberOfGCLogFiles=10 -XX:GCLogFileSize=100M microservice-eureka-server.jar
-XX:+UseGCLogFileRotation -XX:NumberOfGCLogFiles=10 -XX:GCLogFileSize=100M microservice-eureka-server.jar
%t 代表时间
CMS
-Xloggc:d:/gc-cms-%t.log -Xms50M -Xmx50M -XX:MetaspaceSize=256M -XX:MaxMetaspaceSize=256M -XX:+PrintGCDetails -XX:+PrintGCDateStamps -XX:+PrintGCTimeStamps -XX:+PrintGCCause -XX:+UseGCLogFileRotation -XX:NumberOfGCLogFiles=10 -XX:GCLogFileSize=100M
-XX:+UseParNewGC -XX:+UseConcMarkSweepGC
-XX:+UseParNewGC -XX:+UseConcMarkSweepGC
G1
-Xloggc:d:/gc-g1-%t.log -Xms50M -Xmx50M -XX:MetaspaceSize=256M -XX:MaxMetaspaceSize=256M -XX:+PrintGCDetails -XX:+PrintGCDateStamps -XX:+PrintGCTimeStamps -XX:+PrintGCCause -XX:+UseGCLogFileRotation -XX:NumberOfGCLogFiles=10 -XX:GCLogFileSize=100M -XX:+UseG1GC
其他参数
-XX:+DisableExplicitGC
禁用hotspot执行System.gc(),默认禁用
-XX:ReservedCodeCacheSize=<n>[g|m|k]、-XX:InitialCodeCacheSize=<n>[g|m|k]
指定代码缓存的大小
-XX:+UseCodeCacheFlushing
使用该参数让jvm放弃一些被编译的代码,避免代码缓存被占满时JVM切换到interpreted-only的情况
-XX:+DoEscapeAnalysis
开启逃逸分析
-XX:+UseBiasedLocking
开启偏向锁
-XX:+UseLargePages
开启使用大页面
-XX:+PrintTLAB
打印TLAB的使用情况
-XX:TLABSize
设置TLAB大小
通过Java代码获取JVM参数
分析GC日志
GC日志格式
GC类型
不同GC分类的GC细节
老年代使用 CMS GC
新生代使用 Serial GC
DefNew代表新生代使用Serial GC,然后Tenured代表老年代使用Serial Old GC
日志分类
Minor GC
Full GC
GC日志结构剖析
垃圾回收器
GC前后情况
GC时间
Minor GC 日志解析
2020-11-20T17:19:43.265-0800
日志打印时间 日期格式 如 2013-05-04T21:53:59.234+0800
0.822:
gc发生时,Java虚拟机启动以来经过的秒数
[GC(Allocation Failure)
发生了一次垃圾回收,这是一次Minior GC。它不区分新生代还是老年代GC,括号里的内容是gc发生的原因,这里的Allocation Failure的原因是新生代中没有足够区域能够存放需要分配的数据而失败
[PSYoungGen:76800K->8433K(89600K)
PSYoungGen:表示GC发生的区域,区域名称与使用的GC收集器是密切相关的
Serial收集器:Default New Generation 显示Defnew
ParNew收集器:ParNew
Parallel Scanvenge收集器:PSYoung
老年代和新生代同理,也是和收集器名称相关
76800K->8433K(89600K):GC前该内存区域已使用容量->GC后盖区域容量(该区域总容量)
如果是新生代,总容量则会显示整个新生代内存的9/10,即eden+from/to区
如果是老年代,总容量则是全身内存大小,无变化
76800K->8449K(294400K)
在显示完区域容量GC的情况之后,会接着显示整个堆内存区域的GC情况:GC前堆内存已使用容量->GC后堆内存容量(堆内存总容量),并且堆内存总容量 = 9/10 新生代 + 老年代,然后堆内存总容量肯定小于初始化的内存大小
虽然本次是Minor GC,只会进行新生代的垃圾收集,但是也肯定会打印堆中总容量相关信息
,0.0088371
整个GC所花费的时间,单位是秒
[Times:user=0.02 sys=0.01,real=0.01 secs]
user:指CPU工作在用户态所花费的时间
sys:指CPU工作在内核态所花费的时间
real:指在此次事件中所花费的总时间
Full GC 日志解析
2020-11-20T17:19:43.265-0800
日志打印时间 日期格式 如 2013-05-04T21:53:59.234+0800
0.822:
gc发生时,Java虚拟机启动以来经过的秒数
Full GC(Metadata GCThreshold)
括号中是gc发生的原因,原因:Metaspace区不够用了。除此之外,还有另外两种情况会引起Full GC,如下:
- Full GC(FErgonomics) 原因:JVM自适应调整导致的GC
- Full GC(System) 原因:调用了System.gc()方法
[PSYoungGen: 100082K->0K(89600K)]
PSYoungGen:表示GC发生的区域,区域名称与使用的GC收集器是密切相关的
Serial收集器:Default New Generation 显示Defnew
ParNew收集器:ParNew
Parallel Scanvenge收集器:PSYoung
老年代和新生代同理,也是和收集器名称相关
100082K->0K(89600K):GC前该内存区域已使用容量->GC后盖区域容量(该区域总容量)
如果是新生代,总容量则会显示整个新生代内存的9/10,即eden+from/to区
如果是老年代,总容量则是全身内存大小,无变化
ParOldGen:32K->9638K(204800K)
老年代区域没有发生GC,因此本次GC是metaspace引起的
10114K->9638K(294400K)
在显示完区域容量GC的情况之后,会接着显示整个堆内存区域的GC情况:GC前堆内存已使用容量->GC后堆内存容量(堆内存总容量),并且堆内存总容量 = 9/10 新生代 + 老年代,然后堆内存总容量肯定小于初始化的内存大小
[Meatspace:20158K->20156K(1067008K)]
metaspace GC 回收2K空间
GC日志分析工具
GCEasy
子主题
GCViewer
安装
其他工具
宋红康课程大纲
JVM运行情况预估
年轻代对象增长的速率
可以执行命令 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性能的影响。
图灵优化案例
0 条评论
下一页