java基础教程虚拟机性能分析和故障解决工具
--------------作用:帮助判断cpu占用率过高-->死循环、死锁、内存泄露、内存溢出等等问题。
❀❀ 需要掌握 ❀❀: ■ jps 查看进程ID 命令:jps ■ jstat 实时查看java进程运行的数据情况,可以判断是否内存泄露或者内存溢出的情况 命令:jstat -gc 进程ID(pid号) 或者 jstat -gcutil 进程ID ■ jstack ,查看或导出 Java 应用程序中线程堆栈信息,可以判断cpu占用率过高、死循环、死锁的情况 命令:jstack -l 进程ID |
✿ 常识或需要储备知识在文章最后~
一、常用工具详情:
1、jps (JVM Process Status Tool)
■ 列出当前运行的所有java进程的ID
1):疑惑- wins 不是有任务管理器可以查看所有的进程,为啥还要jps?
答:虽然windows任务管理器可以查看所有的进程,是因为当不止一个java进程时,使用win10的任务管理器,很难区分他们;而且win10的工具只能看到进程,看不到线程,所以需要其他工具
2):jps 测试案例:
package jps; import java.io.IOException; /** * ❀掌握:使用 jps命令查看java进程的ID * 命令:jps * 直接输入jps (相当于 jps -V ) * * * 命令格式: jps[options][hostid] 第一个参数options [-mlvV]可以任意组合使用 第二个参数:hostid 主机或者是服务器的ip,如果不指定,就默认为当前的主机或者是服务器。 注意:如果需要查看其他机器上的jvm进程,需要在待查看机器上启动jstatd。 * * jps + * -q: 显示进程ID * -m: 显示进程ID,主类名称,以及传入main方法的参数 * -l: 显示进程ID,主类全名【全限定名】 * -v: 显示进程ID,主类名称,以及传入jvm的参数 * -V: 显示进程ID,主类名称[❀] * @author Huangyujun * *-Xmn8m 指定年轻代内存大小 */ public class jpsDemo { public static void main(String[] args) throws IOException { System.out.println("jps"); System.in.read(); } }
■ 打开终端查看:
■ 测试 jps -m 命令前需要先设置面方法传递参数:
----------【具体步骤】在自己书写的测试类,右键->Run as -> Run Configurations… ->
2、jstat(JVM Statistics Monitoring Tool)
■ 查看虚拟机运行数据------类装载、内存、垃圾收集、JIT编译 -----内存泄露或者内存溢出的判断【使用命令之前需要给虚拟机设置参数】
---因为控制台输入gc日志的前提:配置一下jvm的参数
例如:-Xms20M -Xmx20M -Xmn10M -XX:+UseSerialGC -XX:+PrintGCDetails -verbose:gc -XX:SurvivorRatio=8 * -Xms20M 堆的初始容量 -Xmx20M 堆的最大容量 -Xmn10M 堆中年轻(新生)代的大小 * -XX:+UseSerialGC 指定垃圾收集器SerialGC(串型垃圾回收器) -XX:+PrintGCDetails 打印内存中GC的行为 -verbose:gc 显示GC的情况 * -XX:ServivorRatio=8 设置 New Generation新生代中的 Eden区与Servivor区比例是8:1
❀ 命令: jstat -gc 进程ID 或者 jstat -gcutil 进程ID 判断是否内存泄露或者内存溢出 [这个需要懂点内存jvm收集机制]: 可以看文章(里边有演示怎么判断)--或者那本经典的书《深入理解Java虚拟机》;文章:《奇怪的GC (Allocation Failure)日志 - 简书 (jianshu.io)》 |
■ 案例:
package jstat; import java.io.IOException; /** * -gc 选项:垃圾收集的堆统计信息 S0C、S1C:幸存区容量 S0C:当前幸存者空间0容量(kB) S1C:当前生存空间1的容量(kB) S0U、S1U:❀堆内存中幸存区使用的容量 S0U:幸存者空间0使用大小(kB) S1U:幸存者空间1使用大小(kB) EC、EU:伊甸园容量 伊甸园使用的容量 EC:当前伊甸园空间容量(kB) EU:伊甸园空间使用大小(kB) OC、OU:❀老年代容量、老年代使用的容量 OC:当前的老年代容量(kB) OU:老年代使用大小(kB) 方法区的使用情况:MC、MU:元空间容量、元空间使用的容量 MC:元空间容量(kB) MU:元空间使用大小(kB) 压缩类的情况:CCSC:压缩的类空间容量(kB) CCSU:使用的压缩类空间(kB) ● 垃圾回收:● ● YGC:新生代垃圾收集事件的数量 ● YGCT:新生代垃圾回收时间 ● FGC:完整GC事件的数量 ● FGCT:完整的垃圾收集时间 ● GCT:总垃圾收集时间 ================================================== jstat -gc 线程ID jstat -gcutil 线程ID ■ gc 和 gcutil 区别:gc显示的是大小, gcutil 显示的是占比 =================================================== * @author Huangyujun * -Xms20M -Xmx20M -Xmn10M -XX:+UseSerialGC -XX:+PrintGCDetails -verbose:gc -XX:SurvivorRatio=8 * -Xms20M -Xmx20M -Xmn10M -XX:+UseSerialGC -XX:+PrintGCDetails -verbose:gc * -Xms20M 堆的初始容量 -Xmx20M 堆的最大容量 -Xmn10M 堆中年轻(新生)代的大小 * -XX:+UseSerialGC 指定垃圾收集器SerialGC(串型垃圾回收器) -XX:+PrintGCDetails 打印内存中GC的行为 -verbose:gc 显示GC的情况 * -XX:ServivorRatio=8 设置 New Generation新生代中的 Eden区与Servivor区比例是8:1 */ public class gc_gcutil { //前提:配置一下vm的参数[JVM 配置] //GC(Allocation Failure)[DefNew: 7072k->(956k),0.0042301 secs...][Times:user=0.01 sys=0.00, real=0.01secs]... public static void main(String[] args) throws IOException { final int _1MB = 1024 * 1024; byte[] b1 = new byte[2 * _1MB]; System.out.println("1..."); System.in.read();//阻塞程序,当控制台输入回车键后继续执行 byte[] b2 = new byte[2 * _1MB]; System.out.println("2..."); System.in.read(); byte[] b3 = new byte[2 * _1MB]; System.out.println("3..."); System.in.read(); byte[] a4= new byte[4*_1MB]; } } /** -gcutil 选项: 垃圾收集统计信息 S0:幸存者空间0利用率占该空间当前容量的百分比 S1:幸存者空间1利用率占空间当前容量的百分比 E:Eden空间利用率占空间当前容量的百分比 O:老年代利用率占空间当前容量的百分比 M:元空间利用率占空间当前容量的百分比 CCS:压缩的类空间利用率,以百分比表示 YGC:新生代GC事件的数量 YGCT:新生代垃圾回收时间 FGC:完整GC事件的数量 FGCT:完整的垃圾收集时间 GCT:总垃圾收集时间 */
3、jstack (Stack Trace forJava)
■ 查看或导出 Java 应用程序中线程堆栈信息,可以判断cpu占用率过高、死循环、死锁的情况
❀ 命令:jstack -l 进程ID
1)cpu占用率过高:
1.使用Process Explorer工具,找到CPU占用率高的进程的id;
2.右击该进程,查看属性,在thread选项卡中,找到cpu占用率高的线程id
3.把线程id转换成16进制
4.使用jstack -l 查看进程的线程快照
5.在线程快照中找到指定的线程,并分析代码
----通过工具,知道那个线程占用率高,然后在eclipse或者idea找到该占用率高的线程,从而知道占用率高的代码位置。
图1:使用Process Explorer工具,找到CPU占用率高的进程的id;右击该进程,查看属性,在thread选项卡中,找到cpu占用率高的线程id
图2:把线程id转换成16进制
图3:使用jstack -l 查看进程的线程快照;在线程快照中找到指定的线程,并分析代码
■ 案例(代码):
package jstack; import java.io.IOException; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; /** * jstack应用 * @author Huangyujun * */ public class jstackTest { public static void main(String[] args) { System.out.println("start"); // test1(); // test2(); test3(); System.out.println("end"); } //死循环 public static void test1() { while(true) {} } //等待控制台输入 public static void test2() { try { System.in.read(); } catch (IOException e) { e.printStackTrace(); } } //死锁【我醒来了,我的锁被你拿了,你睡了;你醒来了,你的锁被我拿了,我睡了-------我等你,你等我【等不到的人】】 public static void test3() { Lock lock1 = new ReentrantLock(); Lock lock2 = new ReentrantLock(); new Thread(() -> { try { lock1.lock(); Thread.sleep(100); lock2.lock();//需要加锁,但是加锁失败,锁被另外一个线程myThread2持有 } catch (Exception e) { e.printStackTrace(); } }, "myThread1").start(); new Thread(() -> { try { lock2.lock(); Thread.sleep(100); lock1.lock();//通过lock1加锁,失败,等待锁的释放,按照提示说是被myThread1持有,观察发现:myThread1中:lock1.lock(); Thread.sleep(100); } catch (Exception e) { e.printStackTrace(); } }, "myThread2").start(); } }
//死循环
//等待控制台输入
//死锁
✿ 常识或需要储备知识~
1、System.in.read(); //让程序阻塞
2、Terminal 终端(方便,推荐) 也可以用win10的cmd控制台【dos窗口】
3、控制台输入gc日志的前提:配置一下jvm的参数[JVM 配置]
4、dos命令和进程命令相关的:
■ 常识----dos命令: DOS命令,是操作系统的命令,是一种面向磁盘的操作命令,主要包括 目录操作类命令、磁盘操作类命令、文件操作类命令和其它命令。 |
✿ 常用操作----进程ID的查看与消灭进程: 1,cmd查看所有进程:netstat -ano 2,查看特定端口号的进程[例如:8080]:netstat -ano|findstr 8080 3,杀死对应的PID号对应的线程:taskkill /f /pid PID号 |