②JVM相关调优命令以及工具
2023-05-17 15:44:46 4 举报
AI智能生成
JVM
作者其他创作
大纲/内容
常用命令
jps
jps是jdk提供的一个<font color="#7b1fa2">查看当前java进程的小工具</font>, 可以看做是JavaVirtual Machine Process Status Tool的缩写。非常简单实用。
jps
jps -q
仅仅显示VM 标示,不显示jar,class, main参数等信息.
jps -m
输出主函数传入的参数. 下的hello 就是在执行程序时从命令行输入的参数
jps -v
输出jvm参数
jmp
<font color="#7b1fa2">查看内存信息、实例个数以及内存占用大小</font><br>
①jmap -histo 进程ID
jmap -histo 14660 #查看历史生成的实例<br>jmap -histo:live 14660 #查看当前存活的实例,执行过程中可能会触发一次full gc
输出到文件
查看
num:序号<br><br>instances:实例数量<br><br>bytes:占用空间大小<br><br>class name:类名称,[C is a char[],[S is a short[],[I is a int[],[B is a byte[],[[I is a int[][]
②jmap -heap 进程ID
jmap -heap 14660 #查看堆内存信息
③堆内存dump
jmap -dump:format=b,file=eureka.hprof 14660
图示
也可以设置内存溢出自动导出dump文件(内存很大的时候,可能会导不出来)<br>-XX:+HeapDumpOnOutOfMemoryError<br>-XX:HeapDumpPath=./ (路径)
示例代码
public class OOMTest {<br><br> public static List<Object> list = new ArrayList<>();<br><br> // JVM设置 <br> // -Xms10M -Xmx10M -XX:+PrintGCDetails -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=D:\jvm.dump <br> public static void main(String[] args) {<br> List<Object> list = new ArrayList<>();<br> int i = 0;<br> int j = 0;<br> while (true) {<br> list.add(new User(i++, UUID.randomUUID().toString()));<br> new User(j--, UUID.randomUUID().toString());<br> }<br> }<br>}
使用jvisualvm命令工具导入dump文件查看
如果使用不带选项参数的jmap打印共享对象映射,将会打印目标虚拟机中加载的每个共享对象的起始 地址、 映射大小以及共享对象文件的路径全称。<br><br>-heap 打印java heap摘要<br><br>-histo[:live] 打印堆中的java对象统计信息<br><br>-clstats 打印类加载器统计信息<br><br>-finalizerinfo 打印在f-queue中等待执行finalizer方法的对象<br><br>-dump: 生成java堆的dump文件<br><br> dump-options:<br><br> live 只转储存活的对象,如果没有指定则转储所有对象<br><br> format=b 二进制格式<br><br> file= 转储文件到
jstack
<font color="#7b1fa2">使用jstack加进程ID查找死锁</font><br><br>例如:<br>jstack -l 14660
示例代码
public class DeadLockTest {<br><br> private static Object lock1 = new Object();<br> private static Object lock2 = new Object();<br><br> public static void main(String[] args) {<br> new Thread(() -> {<br> synchronized (lock1) {<br> try {<br> System.out.println("thread1 begin");<br> Thread.sleep(5000);<br> } catch (InterruptedException e) {<br> }<br> synchronized (lock2) {<br> System.out.println("thread1 end");<br> }<br> }<br> }).start();<br><br> new Thread(() -> {<br> synchronized (lock2) {<br> try {<br> System.out.println("thread2 begin");<br> Thread.sleep(5000);<br> } catch (InterruptedException e) {<br> }<br> synchronized (lock1) {<br> System.out.println("thread2 end");<br> }<br> }<br> }).start();<br><br> System.out.println("main thread end");<br> }<br>}
图示1
"Thread-1" 线程名 <br>prio=5 优先级=5<br>tid=0x000000001fa9e000 线程id<br>nid=0x2d64 线程对应的本地线程标识nid<br>java.lang.Thread.State: BLOCKED 线程状态
图示2
jvisualvm查看死锁
使用jstack找出占用CPU最高的线程堆栈信息
示例代码
package com.tuling.jvm;<br><br>/**<br> * 运行此代码,cpu会飙高<br> */<br>public class Math {<br><br> public static final int initData = 666;<br> public static User user = new User();<br><br> public int compute() { //一个方法对应一块栈帧内存区域<br> int a = 1;<br> int b = 2;<br> int c = (a + b) * 10;<br> return c;<br> }<br><br> public static void main(String[] args) {<br> Math math = new Math();<br> while (true){<br> math.compute();<br> }<br> }<br>}
步骤
①使用命令top -p <pid> ,显示你的java进程的内存情况,pid是你的java进程号,比如19663
②按H,获取每个线程的内存情况
③找到内存和cpu占用最高的线程tid,比如19664
④转为十六进制得到 0x4cd0,此为线程id的十六进制表示
注意:这里转换出来的字母是大写的,要转成小写的去查看
⑤执行 jstack 19663|grep -A 10 4cd0,得到线程堆栈信息中 4cd0 这个线程所在行的后面10行,从堆栈中可以发现导致cpu飙高的调用方法<br>
⑥查看对应的堆栈信息找出可能存在问题的代码<br>
jinfo
<font color="#7b1fa2">查看正在运行的Java应用程序的扩展参数 </font><br>
①查看jvm参数<br>jinfo -flags 进程ID
②查看java系统参数<br>jinfo -sysprops 进程ID
jstat
<font color="#7b1fa2">查看堆内存各部分的使用量,以及加载类的数量<br></font><br>jstat [-命令选项] [vmid] [间隔时间(毫秒)] [查询次数]<br>注意:使用的jdk版本是jdk8<br>
①jstat -gc pid <br>垃圾回收统计,最常用,评估程序内存使用及GC压力整体情况
S0C:第一个幸存区的大小,单位KB<br>S1C:第二个幸存区的大小<br>S0U:第一个幸存区的使用大小<br>S1U:第二个幸存区的使用大小<br>EC:伊甸园区的大小<br>EU:伊甸园区的使用大小<br>OC:老年代大小<br>OU:老年代使用大小<br>MC:方法区大小(元空间)<br>MU:方法区使用大小<br>CCSC:压缩类空间大小<br>CCSU:压缩类空间使用大小<br>YGC:年轻代垃圾回收次数<br>YGCT:年轻代垃圾回收消耗时间,单位s<br>FGC:老年代垃圾回收次数 <br>FGCT:老年代垃圾回收消耗时间,单位s<br>GCT:垃圾回收消耗总时间,单位s
②jstat -gccapacity pid<br>堆内存统计
NGCMN:新生代最小容量<br>NGCMX:新生代最大容量<br>NGC:当前新生代容量<br>S0C:第一个幸存区大小<br>S1C:第二个幸存区的大小<br>EC:伊甸园区的大小<br>OGCMN:老年代最小容量<br>OGCMX:老年代最大容量<br>OGC:当前老年代大小<br>OC:当前老年代大小<br>MCMN:最小元数据容量<br>MCMX:最大元数据容量<br>MC:当前元数据空间大小<br>CCSMN:最小压缩类空间大小<br>CCSMX:最大压缩类空间大小<br>CCSC:当前压缩类空间大小<br>YGC:年轻代gc次数<br>FGC:老年代GC次数
③jstat -gcnew pid<br>新生代垃圾回收统计<br>
S0C:第一个幸存区的大小<br>S1C:第二个幸存区的大小<br>S0U:第一个幸存区的使用大小<br>S1U:第二个幸存区的使用大小<br>TT:对象在新生代存活的次数<br>MTT:对象在新生代存活的最大次数<br>DSS:期望的幸存区大小<br>EC:伊甸园区的大小<br>EU:伊甸园区的使用大小<br>YGC:年轻代垃圾回收次数<br>YGCT:年轻代垃圾回收消耗时间
④jstat -gcnewcapacity pid<br>新生代内存统计
NGCMN:新生代最小容量<br>NGCMX:新生代最大容量<br>NGC:当前新生代容量<br>S0CMX:最大幸存1区大小<br>S0C:当前幸存1区大小<br>S1CMX:最大幸存2区大小<br>S1C:当前幸存2区大小<br>ECMX:最大伊甸园区大小<br>EC:当前伊甸园区大小<br>YGC:年轻代垃圾回收次数<br>FGC:老年代回收次数
⑤jstat -gcold pid<br>老年代内存回收统计
MC:方法区大小<br>MU:方法区使用大小<br>CCSC:压缩类空间大小<br>CCSU:压缩类空间使用大小<br>OC:老年代大小<br>OU:老年代使用大小<br>YGC:年轻代垃圾回收次数<br>FGC:老年代垃圾回收次数<br>FGCT:老年代垃圾回收消耗时间<br>GCT:垃圾回收消耗总时间
⑥jstat -gcoldcapacity pid<br>老年代内存统计
OGCMN:老年代最小容量<br>OGCMX:老年代最大容量<br>OGC:当前老年代大小<br>OC:老年代大小<br>YGC:年轻代垃圾回收次数<br>FGC:老年代垃圾回收次数<br>FGCT:老年代垃圾回收消耗时间<br>GCT:垃圾回收消耗总时间
⑦jstat -gcmetacapacity pid<br>元空间统计
MCMN:最小元数据容量<br>MCMX:最大元数据容量<br>MC:当前元数据空间大小 <br>CCSMN:最小压缩类空间大小<br>CCSMX:最大压缩类空间大小<br>CCSC:当前压缩类空间大小<br>YGC:年轻代垃圾回收次数<br>FGC:老年代垃圾回收次数<br>FGCT:老年代垃圾回收消耗时间<br>GCT:垃圾回收消耗总时间
⑧jstat -gcutil pid
S0:幸存1区当前使用比例<br>S1:幸存2区当前使用比例<br>E:伊甸园区使用比例<br>O:老年代使用比例<br>M:元数据区使用比例<br>CCS:压缩使用比例<br>YGC:年轻代垃圾回收次数<br>FGC:老年代垃圾回收次数<br>FGCT:老年代垃圾回收消耗时间<br>GCT:垃圾回收消耗总时间
内存泄漏是怎么回事
一般电商架构可能会使用多级缓存架构,就是redis加上JVM级缓存,可能为了图方便对于JVM级缓存就简单使用一个hashmap,于是不断往里面放缓存数据,但是很少考虑这个map的容量问题,结果这个缓存map越来越大,一直占用着老年代的很多空间,时间长了就会导致full gc非常频繁,这就是一种内存泄漏,对于一些老旧数据没有及时清理导致一直占用着宝贵的内存资源,时间长了除了导致full gc,还有可能导致OOM。<br>这种情况完全可以考虑采用一些成熟的JVM级缓存框架来解决,比如ehcache等自带一些LRU数据淘汰算法的框架来作为JVM级的缓存。
工具
Arthas
Arthas 是 Alibaba 在 2018 年 9 月开源的 Java 诊断工具。支持 JDK6+, 采用命令行交互模式,可以方便的定位和诊断线上程序运行问题。Arthas 官方文档十分详细,详见:https://alibaba.github.io/arthas
Arthas使用场景<br>得益于 Arthas 强大且丰富的功能,让 Arthas 能做的事情超乎想象。下面仅仅列举几项常见的使用情况,更多的使用场景可以在熟悉了 Arthas 之后自行探索。
(1)是否有一个全局视角来查看系统的运行状况?<br>(2)为什么 CPU 又升高了,到底是哪里占用了 CPU ?<br>(3)运行的多线程有死锁吗?有阻塞吗?<br>(4)程序运行耗时很长,是哪里耗时比较长呢?如何监测呢?<br>(5)这个类从哪个 jar 包加载的?为什么会报各种类相关的 Exception?<br>(6)我改的代码为什么没有执行到?难道是我没 commit?分支搞错了?<br>(7)遇到问题无法在线上 debug,难道只能通过加日志再重新发布吗?<br>(8)有什么办法可以监控到 JVM 的实时运行状态?
Arthas使用<br>
# github下载arthas<br>wget https://alibaba.github.io/arthas/arthas-boot.jar<br><br># 或者 Gitee 下载<br>wget https://arthas.gitee.io/arthas-boot.jar
用java -jar运行即可,可以识别机器上所有Java进程(我们这里之前已经运行了一个Arthas测试程序,代码见下方)
Arthas<br>查看模拟 CPU 过高<br>模拟线程死锁<br>不断的向 hashSet 集合增加数据<br>
①选择进程序号1,进入进程信息操作<br>
②输入dashboard可以查看整个进程的运行情况,线程、内存、GC、运行环境信息<br>
③输入thread可以查看线程详细情况<br>
④输入 thread -b 可以查看线程死锁<br>
⑤输入 jad加类的全名 可以反编译,这样可以方便我们查看线上代码是否是正确的版本<br>
⑥使用 ognl 命令可以查看线上系统变量的值,甚至可以修改变量的值<br>
收藏
0 条评论
下一页