JVM
一、JVM常用指令
1、jps(Java Process Status)
jps
-l:输出进程的全类名,如果是jar包,则输出jar完整路径
-m:输出虚拟机进程启动时传递给主类main()的参数
-v:列出虚拟机进程启动时的jvm参数
注意:如果某java进程关闭了默认开启的UsePerfData参数(即使用参数-XX:-UsePerfData),那么jps命令(以及jstat)将无法探知该进程
2、jstat(Java Virtual Machine statistics monitoring tool)
jstat是用于监视虚拟机各种运行状态信息的命令行工具。它可以显示本地或者远程虚拟机进程中的类装载、内存、垃圾收集、JIT编译等运行数据。在没有GUI图形界面时,它将是运行期定位虚拟机性能问题的首选工具,常用于检测垃圾回收问题以及内存泄漏问题。
(1)jstat -gc pid 1000 10
每隔1秒打印1次,共打印10次
各个参数含义:
新生代相关:
S0C:幸存区一的大小(字节)
S1C:幸存区二的大小(字节)
S0U:幸存区一已使用的大小(字节)
S1U:幸存区二已使用的大小(字节)
EC: Eden空间的大小(字节)
EU:Eden空间已使用的大小(字节)
老年代相关:
OC:老年代的大小(字节)
OU:老年代已使用的大小(字节)
方法区(元空间)相关:
MC:方法区的大小
MU:方法区已使用的大小
CCSC:压缩类空间的大小
CCSU:压缩类空间已使用的大小
其它
YGC:是指从启动到当前时刻young gc次数
YGCT:是指从启动到当前时刻young gc消耗的时间(秒)
FGC: 是指从启动到当前时刻full gc次数
FGCT:是指从启动到当前时刻full gc消耗的时间(秒)
GCT:是指从用启动到当前时刻gc消耗的总时间
(2)jstat -gccapacity pid 1000 10
显示与-gc基本相同,但输出关注Java各个区域使用到的最大,最小空间。
(3)jstat -gcutil pid 1000 10
显示内容与-gc基本相同,输出百分占比,例如:
(4)jstat -gccause pid 1000 10
该命令会额外展示gc产生的原因
(5)jstat -gcnew pid 1000 10
-gcnew:显示新生代gc状况
-gcnewcapacity:与上面基本相同,主要关注使用到的最大、最小空间
(6)jstat -gcold pid 1000 10
-gcold:显示老年代gc状况
-geoldcapacity:与gcold基本相同,注意关注使用到的最大、最小空间
(7)jstat -gcpermcapacity pid 1000 10
该命令显示永久代使用到的最大最小空间。
(8)使用jstat排查内存泄露
3、jinfo(Java Virtual Machine Configuration Information)
jinfo是jdk提供的一个可以实时查看和修改java虚拟机各种配置参数和系统属性的命令工具。使用jps -v
可以查看Java虚拟机启动时显示指定的参数,查看没有显示指定的参数就可以使用jinfo指令进行查看。
(1)jinfo -flag name pid
查看指定的参数
jinfo -flag MaxHeapSize 123 --查看进程123的堆区的最大值
(2)jinfo -flag name=value pid
修改指定名称的参数,不需要重启jvm虚拟机
jinfo -flag MaxHeapSize=2g 123 --设置进程123的堆区最大为2g
(3)jinfo -flags pid 显示全部的配置参数
(4)jinfo -sysprops pid 以键值对的方式展示jvm虚拟机的全部属性
4、jmap(JVM Meroy Map)
jamp的作用一方面是获取dump文件(堆转储快照文件,二进制文件),它还可以获取目标java进程的内存相关信息,包含java堆区各区域的使用情况,堆中对象的统计信息,类加载信息等
(1)jmap -heap pid 查看堆相关信息
(4)jmap -histo[:live] pid 显示堆中对象的统计信息
(3)jmap -dump:live,format=b,file=filename.hprof pid
生成dump文件
live:仅将存活的对象dump出
format:编码格式
flie:生成的文件名和路径
二、排查案例
1、 “线程出现死锁”、“内存占用居高不下”、“CPU占用居高不下”等问题,如何定位到具体的线程
-
常见的出现“CPU居高不下”的原因:
(1)属于计算密集型任务,大量计算会占用CPU
(2) 程序进入了死循环
(3)频繁gc,主要是gc线程占用着CPU -
排查步骤:
(1)、使用top命令查看每个进程的运行情况,CPU,内存等指标;详见top命令详解
(2)、在上一步观察到了某个进程运行异常后,可以使用top -H -p pid
命令进行查看具体每个线程的运行情况
(3)、使用jstack pid > a.txt
打印线程的堆栈信息
(4)、将第2步中运行异常的线程id转为16进制,比如上图的4807占用CPU最高,那这个时候可以使用printf "%x\n" pid
,将线程id转换为16进制
(5)、到生成的文件中搜索转换出来的线程id,查看线程的堆栈信息
2、 “内存泄露”、“内存溢出”相关问题排查
内存泄露
内存泄露是指,申请的内存空间没有被使用,且长期没有被释放,导致一直占用着堆内存。值得注意的是这些没有被及时释放的内存一定是“可达的”,如果“不可达”就会被gc了。
内存溢出
内存溢出是指,在申请内存时,由于堆内存小于申请的内存,导致报错java.lang.OutOfMemoryError。
-
常见的出现“内存泄露”的原因:
(1)循环过多或者死循环,产生大量对象
(2)静态集合引起的内存泄露,因为静态集合的生命周期和JVM一致,所以静态集合引用的对象不能被释放
(3)连接建立过多,且未正常释放,比如socket连接,数据库连接等
(4)内部类的对象被长期持有,那么内部类所属的外部类对象也不会被回收
(5)HashMap中存入大量数据,当HashMap中的key的hash值发生改变,导致无法获取到原来的值,也无法删除
(6)堆外内存泄露,类加载过多 -
排查步骤:
(1)使用jmap -heap pid,查看一下堆区的配置和使用情况,是否有异常
(2)使用jmap -histo:[live] pid,查看对象的统计信息,可以找到占用内存异常的对象
(3)使用jmap -dump:live,format=b,file=filename.hprof pid,导出堆内存快照
(4)可以使用eclipse的MAT工具分析快照文件,然后找出占用内存异常的对象,可以通过查看对象之间的引用,定位到最终的代码