Rubin's Blog

  • 首页
  • 关于作者
  • 隐私政策
享受恬静与美好~~~
分享生活的点点滴滴~~~
  1. 首页
  2. JVM
  3. 正文

JVM常用指令与可视化调优工具

2022年 1月 4日 792点热度 0人点赞 0条评论

常用指令

jps

jps 是(java process Status Tool), Java版的ps命令,查看java进程及其相关的信息,如果你想找到一个java进程的pid,那可以用jps命令替代linux中的ps命令了,简单而方便。

命令格式:

jps [options] [hostid]

options参数解释:

  • -l:显示进程id,显示主类全名或jar路径
  • -q:显示进程id
  • -m:显示进程id,显示JVM启动时传递给main()的参数
  • -v:显示进程id,显示JVM启动时显示指定的JVM参数

hostid : 主机或其他服务器ip

最常用示例:

jps -l 输出jar包路径,类全名
jps -m 输出main参数
jps -v 输出JVM参数

jinfo

jinfo是用来查看JVM参数和动态修改部分JVM参数的命令。

命令格式:

jinfo [option] <pid>

options参数解释:

  • no options 输出所有的系统属性和参数
  • -flag 打印指定名称的参数
  • -flag [+|-] 打开或关闭参数
  • -flag = 设置参数
  • -flags 打印所有参数
  • -sysprops 打印系统配置

最常用示例:

其中11666为pid

查看JVM参数和系统配置:

jinfo 11666
jinfo -flags 11666
jinfo -sysprops 11666

查看打印GC日志参数:

jinfo -flag PrintGC 11666
jinfo -flag PrintGCDetails 11666

打开GC日志参数:

jinfo -flag +PrintGC 11666
jinfo -flag +PrintGCDetails 11666

关闭GC日志参数:

jinfo -flag -PrintGC 11666
jinfo -flag -PrintGCDetails 11666

还可以使用下面的命令查看那些参数可以使用jinfo命令来管理:

常用JVM参数:

-Xms:初始堆大小,默认为物理内存的1/64(<1GB);默认(MinHeapFreeRatio参数可以调整)空余堆内存小于40%时,JVM就会增
大堆直到-Xmx的最大限制
-Xmx:最大堆大小,默认(MaxHeapFreeRatio参数可以调整)空余堆内存大于70%时,JVM会减少堆直到 -Xms的最小限制
-Xmn:新生代的内存空间大小,注意:此处的大小是(eden+ 2 survivor space)。与jmap -heap中显示的New gen是不同
的。整个堆大小=新生代大小 + 老生代大小 + 永久代大小
在保证堆大小不变的情况下,增大新生代后,将会减小老生代大小。此值对系统性能影响较大,Sun官方推荐配置为整个堆的3/8
-XX:SurvivorRatio:新生代中Eden区域与Survivor区域的容量比值,默认值为8。两个Survivor区与一个Eden区的比值为
2:8,一个Survivor区占整个年轻代的1/10
-Xss:每个线程的堆栈大小。JDK5.0以后每个线程堆栈大小为1M,以前每个线程堆栈大小为256K。应根据应用的线程所需内存大小
进行适当调整。在相同物理内存下,
减小这个值能生成更多的线程。但是操作系统对一个进程内的线程数还是有限制的,不能无限生成,经验值在3000~5000左右。一般
小的应用, 如果栈不是很深, 应该是128k够用的,
大的应用建议使用256k。这个选项对性能响比较大,需要严格的测试。和threadstacksize选项解释很类似,官方文档似乎没有
解释,
在论坛中有这样一句话:"-Xss ``is translated ``in a VM flag named ThreadStackSize”一般设置这个值就可以了
-XX:PermSize:设置永久代(perm gen)初始值。默认值为物理内存的1/64
-XX:MaxPermSize:设置持久代最大值。物理内存的1/4

jstat

jstat命令是使用频率比较高的命令,主要用来查看JVM运行时的状态信息,包括内存状态、垃圾回收等。

命令格式:

jstat [option] VMID [interval] [count]

其中VMID是进程id,interval是打印间隔时间(毫秒),count是打印次数(默认一直打印)。

option参数解释:

  • -class:class loader的行为统计
  • -compiler:HotSpt JIT编译器行为统计
  • -gc:垃圾回收堆的行为统计
  • -gccapacity:各个垃圾回收代容量(young,old,perm)和他们相应的空间统计
  • -gcutil:垃圾回收统计概述
  • -gccause:垃圾收集统计概述(同-gcutil),附加最近两次垃圾回收事件的原因
  • -gcnew:新生代行为统计
  • -gcnewcapacity:新生代与其相应的内存空间的统计
  • -gcold:年老代和永生代行为统计
  • -gcoldcapacity:年老代行为统计
  • -printcompilation:HotSpot编译方法统计

常用示例及打印字段解释:

jstat -gcutil 11666 1000 3

11666为pid,每隔1000毫秒打印一次,打印3次。

字段解释:

  • S0:survivor0使用百分比
  • S1:survivor1使用百分比
  • E:Eden区使用百分比
  • O:老年代使用百分比
  • M:元数据区使用百分比
  • CCS:压缩使用百分比
  • YGC:年轻代垃圾回收次数
  • YGCT:年轻代垃圾回收消耗时间
  • FGC:Full GC垃圾回收次数
  • FGCT:Full GC垃圾回收消耗时间
  • GCT:垃圾回收消耗总时间
jstat -gc 11666 1000 3

-gc和-gcutil参数类似,只不过输出字段不是百分比,而是实际的值。

输出:

字段解释:

  • S0C:survivor0大小
  • S1C:survivor1大小
  • S0U:survivor0已使用大小
  • S1U:survivor1已使用大小
  • EC:Eden区大小
  • EU:Eden区已使用大小
  • OC:老年代大小
  • OU:老年代已使用大小
  • MC:方法区大小
  • MU:方法区已使用大小
  • CCSC:压缩类空间大小
  • CCSU:压缩类空间已使用大小
  • YGC:年轻代垃圾回收次数
  • YGCT:年轻代垃圾回收消耗时间
  • FGC:Full GC垃圾回收次数
  • FGCT:Full GC垃圾回收消耗时间
  • GCT:垃圾回收消耗总时间

jstack

jstack是用来查看JVM线程快照的命令,线程快照是当前JVM线程正在执行的方法堆栈集合。使用jstack命令可以定
位线程出现长时间卡顿的原因,例如死锁,死循环等。jstack还可以查看程序崩溃时生成的core文件中的stack信息。

命令格式:

jstack [options] <pid>

option参数解释:

  • -F:当使用jstack无响应时,强制输出线程堆栈
  • -m:同时输出java堆栈和c/c++堆栈信息(混合模式)
  • -l:除了输出堆栈信息外,还显示关于锁的附加信息

cpu占用过高问题

  • 使用Process Explorer工具找到cpu占用率较高的线程
  • 在thread卡中找到cpu占用高的线程id
  • 线程id转换成16进制
  • 使用jstack -l 查看进程的线程快照
  • 线程快照中找到指定线程,并分析代码

jstack检查死锁问题

/**
 * jstack [options]  <pid>
 * <p>
 * option参数解释:
 * - -F 当使用jstack <pid>无响应时,强制输出线程堆栈。
 * - -m 同时输出java堆栈和c/c++堆栈信息(混合模式)
 * - -l 除了输出堆栈信息外,还显示关于锁的附加信息
 */
public class DeadLock {
    private static Object obj1 = new Object();
    private static Object obj2 = new Object();

    public static void main(String[] args) {
        new Thread(new Thread1()).start();
        new Thread(new Thread2()).start();
    }

    private static class Thread1 implements Runnable {
        public void run() {
            synchronized (obj1) {
                System.out.println("Thread1 拿到了 obj1 的锁!");
                try {
                // 停顿2秒的意义在于,让Thread2线程拿到obj2的锁
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                synchronized (obj2) {
                    System.out.println("Thread1 拿到了 obj2 的锁!");
                }
            }
        }
    }

    private static class Thread2 implements Runnable {
        public void run() {
            synchronized (obj2) {
                System.out.println("Thread2 拿到了 obj2 的锁!");
                try {
                    // 停顿2秒的意义在于,让Thread1线程拿到obj1的锁
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                synchronized (obj1) {
                    System.out.println("Thread2 拿到了 obj1 的锁!");
                }
            }
        }
    }
}

执行指令:

jstack -l 11666

打印结果:

ound one Java-level deadlock:
=============================
"Thread-1":
waiting to lock monitor 0x00007efc880062c8 (object 0x00000000ec1dc5c8, a java.lang.Object),
which is held by "Thread-0"
"Thread-0":
waiting to lock monitor 0x00007efc88004e28 (object 0x00000000ec1dc5d8, a java.lang.Object),
which is held by "Thread-1"
Java stack information for the threads listed above:
===================================================
"Thread-1":
   at DeadLock$Thread2.run(DeadLock.java:35)
    - waiting to lock <0x00000000ec1dc5c8> (a java.lang.Object)
    - locked <0x00000000ec1dc5d8> (a java.lang.Object)
   at java.lang.Thread.run(Thread.java:748)
"Thread-0":
   at DeadLock$Thread1.run(DeadLock.java:19)
    - waiting to lock <0x00000000ec1dc5d8> (a java.lang.Object)
    - locked <0x00000000ec1dc5c8> (a java.lang.Object)
   at java.lang.Thread.run(Thread.java:748)
Found 1 deadlock.

jmap

jmap可以生成 java程序的dump文件, 也可以查看堆内对象示例的统计信息、查看ClassLoader的信息以及finalizer 队列。

命令格式:

jmap [option] (连接正在执行的进程)

option参数解释:

  • 如果使用不带选项参数的jmap打印共享对象映射,将会打印目标虚拟机中加载的每个共享对象的起始 地址、映射大小以及共享对象文件的路径全称
  • -heap:打印java heap摘要
  • -histo[:live]:打印堆中的java对象统计信息
  • -clstats:打印类加载器统计信息
  • -finalizerinfo:打印在f-queue中等待执行finalizer方法的对象
  • -dump:生成java堆的dump文件

dump-options:

  • live:只转储存活的对象,如果没有指定则转储所有对象
  • format=b:二进制格式
  • file=xxx.bin:转储文件到

常用示例:

jmap -dump:live,format=b,file=dump.bin 11666

输出:

Dumping heap to /dump.bin ...
Heap dump file created

这个命令是要把java堆中的存活对象信息转储到dump.bin文件。

jmap -finalizerinfo 11666

输出:

Attaching to process ID 11666, please wait...
Debugger attached successfully.
Server compiler detected.
JVM version is 24.71-b01
Number of objects pending for finalization: 0

输出结果的含义为当前没有在等待执行finalizer方法的对象。

jmap -heap 11666

输出堆的详细信息:

Attaching to process ID 11666, please wait...
Debugger attached successfully.
Server compiler detected.
JVM version is 25.25-b02
using thread-local object allocation.
Parallel GC with 4 thread(s)
Heap Configuration: //堆内存初始化配置
 MinHeapFreeRatio  = 0 //对应jvm启动参数-XX:MinHeapFreeRatio设置JVM堆最小空闲比率(default 40)
 MaxHeapFreeRatio  = 100 //对应jvm启动参数 -XX:MaxHeapFreeRatio设置JVM堆最大空闲比率(default 70)
 MaxHeapSize= 1073741824 (1024.0MB) //对应jvm启动参数-XX:MaxHeapSize=设置JVM堆的最大大小
 NewSize   = 22020096 (21.0MB) //对应jvm启动参数-XX:NewSize=设置JVM堆的新生代的默认大小
 MaxNewSize = 357564416 (341.0MB) //对应jvm启动参数-XX:MaxNewSize=设置JVM堆的新生代的最大大小
 OldSize   = 45088768 (43.0MB) //对应jvm启动参数-XX:OldSize=<value>:设置JVM堆的老年代的大小
 NewRatio  = 2 //对应jvm启动参数-XX:NewRatio=:新生代和老生代的大小比率
 SurvivorRatio   = 8 //对应jvm启动参数-XX:SurvivorRatio=设置新生代中Eden区与Survivor区的大小比值
 MetaspaceSize   = 21807104 (20.796875MB) // 元数据区大小
 CompressedClassSpaceSize = 1073741824 (1024.0MB) //类压缩空间大小
 MaxMetaspaceSize  = 17592186044415 MB //元数据区最大大小
 G1HeapRegionSize  = 0 (0.0MB) //G1垃圾收集器每个Region大小
Heap Usage: //堆内存使用情况
PS Young Generation
Eden Space: //Eden区内存分布
 capacity = 17825792 (17.0MB) //Eden区总容量
 used   = 12704088 (12.115562438964844MB) //Eden区已使用
 free   = 5121704 (4.884437561035156MB) //Eden区剩余容量
 71.26801434685203% used //Eden区使用比率
From Space: //其中一个Survivor区的内存分布
 capacity = 2097152 (2.0MB)
 used   = 1703936 (1.625MB)
 free   = 393216 (0.375MB)
 81.25% used
To Space: //另一个Survivor区的内存分布
 capacity = 2097152 (2.0MB)
 used   = 0 (0.0MB)
 free   = 2097152 (2.0MB)
 0.0% used
PS Old Generation
 capacity = 52428800 (50.0MB) //老年代容量
 used   = 28325712 (27.013504028320312MB) //老年代已使用
 free   = 24103088 (22.986495971679688MB) //老年代空闲
 54.027008056640625% used //老年代使用比率
15884 interned Strings occupying 2075304 bytes.
jmap -histo:live 11666 | more

输出存活对象统计信息:

jhat

jhat是用来分析jmap生成dump文件的命令,jhat内置了应用服务器,可以通过网页查看dump文件分析结果,jhat一般是用在离线分析上。

命令格式:

jhat [option][dumpfile]

option参数解释:

  • -stack false:关闭对象分配调用堆栈的跟踪
  • -refs false:关闭对象引用的跟踪
  • -port:HTTP服务器端口,默认是7000
  • -debug:debug级别
  • -version:分析报告版本

常用示例:

jhat dump.bin

JVM常用工具

Jconsole监控管理工具

Jconsole(Java Monitoring and Management Console)是从java5开始,在JDK中自带的java监控和管理控制台,用于对JVM中内存,线程和类等的监控,是一个基于JMX(java management extensions)的GUI性能监测工具。jconsole使用jvm的扩展机制获取并展示虚拟机中运行的应用程序的性能和资源消耗等信息。

直接在jdk/bin目录下点击jconsole.exe即可启动。

内存监控

代码准备:

import java.util.*;
public class JConsoleDemo {
 
  static class OOMObject {
    public byte[] placeholder = new byte[64 * 1024];
   }
  public static void fillHeap(int num) throws InterruptedException {
    List<OOMObject> list = new ArrayList<OOMObject>();
    for (int i = 0; i < num; i++) {
      Thread.sleep(50);
      list.add(new OOMObject());
     }
    System.gc();
   }
  public static void main(String[] args) throws Exception {
    fillHeap(1000);
    //System.gc();
    Thread.sleep(10000);
   }
}

编译运行JConsoleDemo类, 运行时设置的虚拟机参数为 -Xms100m -Xmx100m -XX:+UseSerialGC ,在%JAVA_HOME%\bin目录下, 启动jconsole.exe , 将自动搜索出本机运行的所有虚拟机进程, 这里我们选择JConsoleDemo对应的进程2464。

启动后主界面如下:

在"内存"页签,查看堆内存Eden区的运行趋势如下:

从图中详细信息可以看出,Eden区的内存大小为27.328KB,所以折线图中显示每次到27Mb左右时系统就会进行一次GC。当1000次循环结束后,执行System.gc(),柱状图中显示Eden区和Survivor区基本被清空,但老年代的对应柱状图仍保持峰值状态,这是因为System.gc()是在fillHeap()方法内执行,所以list对象在System.gc()执行时仍然是存活的( 处于
作用域之内、被引用)。如果将System.gc()移动到fillHeap()方法外执行,如下柱状图所示,则会回收包括老年代的所有内存。

线程监控

查看CPU使用率及活锁阻塞线程:

public class Demo7_JConsole02 {
    /**
     * 线程死循环演示
     */
    public static void createBusyThread() {
        Thread thread = new Thread(new Runnable() {
            public void run() {
                while (true);
            }
        }, "testBusyThread");
        System.out.println("启动testBusyThread 线程完毕..");
        thread.start();
    }

    /**
     * 线程锁等待演示
     */
    public static void createLockThread(final Object lock) {
        Thread thread = new Thread(new Runnable() {
            public void run() {
                synchronized (lock) {
                    try {
                        lock.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }, "testLockThread");

        thread.start();
        System.out.println("启动testLockThread 线程完毕..");
    }

    public static void main(String[] args) throws Exception {
        System.out.println("main 线程..");
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        System.out.println("redLine阻塞");
        br.readLine();
        createBusyThread();
        System.out.println("redLine阻塞");
        br.readLine();
        Object obj = new Object();
        createLockThread(obj);
        System.out.println("main 线程结束..");
    }
}

查看死锁线程:

public class Demo7_JConsole03 {
    /**
     * 线程死锁等待演示
     */
    static class SynAddRunalbe implements Runnable {
        int a, b;
        public SynAddRunalbe(int a, int b) {
            this.a = a;
            this.b = b;
        }

        public void run() {
            synchronized (Integer.valueOf(a)) {
                synchronized (Integer.valueOf(b)) {
                    System.out.println(a + b);
                }
            }
        }
    }

    public static void main(String[] args) {
        for (int i = 0; i < 100; i++) {
            new Thread(new SynAddRunalbe(1, 2)).start();
            new Thread(new SynAddRunalbe(2, 1)).start();
        }
    }
}

编译运行,在"线程"页签可查看"死锁"描述。这是因为1、2两个数值在Integer类的缓存常量池[-128, 127]范围内,这样当多次调用Integer.valueOf()方法时,不会再每次都创建对象,而是直接返回缓存常量池中的对象。所以上面两个线程的同步代码块中实际上只创建了两个锁对象,且在某一时刻互相持有对方的锁,即"死锁"现象。

VisualVM可视化优化工具

简介

VisualVM 是一个工具,它提供了一个可视界面,用于查看 Java 虚拟机 (Java Virtual Machine, JVM) 上运行的基于Java 技术的应用程序(Java 应用程序)的详细信息。VisualVM 对 Java Development Kit (JDK) 工具所检索的 JVM 软件相关数据进行组织,并通过一种使您可以快速查看有关多个 Java 应用程序的数据的方式提供该信息。您可以查看本地应用程序以及远程主机上运行的应用程序的相关数据。此外,还可以捕获有关 JVM 软件实例的数据,并将该数据保存到本地系统,以供后期查看或与其他用户共享。

概述与插件安装

VisualVM基于NetBeans平台开发,因此它一开始就具备了插件扩展的特性,通过插件支持,VisualVM可以做许多事情,例如:

  • 显示虚拟机进程和进程的配置、环境信息(jps、jinfo)
  • 监视应用程序的CPU、GC、堆、方法区及线程的信息(jstat、jstack)
  • dump及分析堆转储快照(jmap、jhat)
  • 方法级的程序运行性能分析, 找出被调用最多、运行时间最长的方法
  • 离线程序快照:收集程序的运行时配置、线程dump、内存dump等信息建立一个快照, 可以将快照发送开发者处进行bug反馈等等

在%JAVA_HOME%\bin目录下,启动jvisualvm.exe进入主界面,点击"工具"→"插件"→"可用插件"选项,选择所需的插件安装。

安装好插件后,选择一个正在运行的java程序就可以查看程序监控的主界面了:

堆转储快照

两种方式生成堆dump文件:

  • 在"应用程序"窗口中右键单击应用程序节点, 选择"堆 Dump"
  • 在"监视"页签中选择"堆 Dump"

分析程序性能

在Profiler页签中,可以对程序运行期间方法级的CPU和内存进行分析,这个操作会对程序运行性能有很大影响,所以一般不再生产环境使用。CPU分析将会统计每个方法的执行次数、执行耗时; 内存分析则会统计每个方法关联的对象数及对象所占空间。

以上就是本文的全部内容了。欢迎小伙伴们积极留言交流~~~

本作品采用 知识共享署名 4.0 国际许可协议 进行许可
标签: JVM
最后更新:2022年 6月 9日

RubinChu

一个快乐的小逗比~~~

打赏 点赞
< 上一篇
下一篇 >

文章评论

razz evil exclaim smile redface biggrin eek confused idea lol mad twisted rolleyes wink cool arrow neutral cry mrgreen drooling persevering
取消回复
文章目录
  • 常用指令
    • jps
    • jinfo
    • jstat
    • jstack
      • cpu占用过高问题
      • jstack检查死锁问题
    • jmap
    • jhat
  • JVM常用工具
    • Jconsole监控管理工具
      • 内存监控
      • 线程监控
    • VisualVM可视化优化工具
      • 简介
      • 概述与插件安装
      • 堆转储快照
      • 分析程序性能
最新 热点 随机
最新 热点 随机
问题记录之Chrome设置屏蔽Https禁止调用Http行为 问题记录之Mac设置软链接 问题记录之JDK8连接MySQL数据库失败 面试系列之自我介绍 面试总结 算法思维
Kafka之源码环境搭建 MongoDB之应用实战 Tomcat之类加载机制 java面试系列之引用 SpringCloud之常见问题及解决方案 JVM简介

COPYRIGHT © 2021 rubinchu.com. ALL RIGHTS RESERVED.

Theme Kratos Made By Seaton Jiang

京ICP备19039146号-1