【JVM实战】基于JDK命令行工具的监控

基于JDK命令行工具的监控

一、JVM的参数类型

JVM的参数类型主要分成三类

  • 标准参数
  • X参数
  • XX参数

标准参数,在JVM的各个版本中基本不变的(尽可能保持兼容),是相对比较稳定的参数。

比方说,大家在第一次安装Java后,都会敲的命令行

java  -version

在这里插入图片描述
里面会显示JVM的一些基本信息,比如版本号,和编译方式,这里的minxed mode表示混合编译,还有Server VM 表示JVM的运行模式是Server模式。

除了-version还有-help

java -help

它可以列举一些Java的标准参数,并标有解释。
在这里插入图片描述
下面还有很多,这里仅仅列举一部分。

这里说两个参数: -server 和 -client

JVM有两种运行模式Server与Client。两种模式的区别在于,Client模式启动速度较快,Server模式启动较慢;但是启动进入稳定期长期运行之后Server模式的程序运行速度比Client要快很多。这是因为Server模式启动的JVM采用的是重量级的虚拟机,对程序采用了更多的优化;而Client模式启动的JVM采用的是轻量级的虚拟机。

server:默认为堆提供了一个更大的空间和并行的垃圾收集器 并且在运行时可以更大程度的优化代码

client:客户端虚拟机有较小的默认堆内存 可以缩短JVM启动的时间和占用更少的内存 客户端的JVM只有在32位操作系统中才有

值得我们关注的是:

  1. 从JDK5开始 当应用启动时会检测当前的运行环境是否是服务器 如果是服务器就使用Server JVM
  2. 在JDK6中 Server JVM要求至少双核CPU和2GB物理内存
  3. 在32位操作系统上 JDK可以运行Server JVM 但是JRE只能运行Client JVM

这些了解即可,一般不会变动。

X参数

X参数和XX参数都是非标准化参数,在各个JVM版本中可能会变化。

不过X参数相对于XX参数,使用频率低很多,下面看几个常见的。

设置JVM的编译方式:

  • -Xint:解释执行
  • -Xcomp:第一次使用就编译成本地代码
  • -Xmixed:混合模式,JVM自己来决定是否如何编译本地代码

使用演示:
在这里插入图片描述
在这里插入图片描述
这里再还原成混合模式:
在这里插入图片描述

默认的混合模式 是最好的。

XX参数

XX参数主要分为两大类:

Boolean类型

格式:

-XX:[+-] <name> 表示启用或者禁用了name参数 +表示启用

比如:

  • -XX:+UseConcMarkSweepGC表示启用了CMS垃圾回收器
  • -XX:+UseG1GC 表示启用了G1垃圾回收器

非Boolean(Key-Value类型)

格式:

-XX:<name>=<value> 表示name参数的值是value

比如:

  • -XX:MaxGCPauseMills=500表示GC最大的停顿时间是500
  • ​ -XX:GCTimeRatio=19

GCTimeRatio参数的值应当是一个大于0且小于100的整数,也就是垃圾收集时间占总时间的比率,相当于是吞吐量的倒数。如果把此参数设置为19,那允许的最大GC时间就占总时间的5%(即1/(1+19)),默认值为99,就是允许最大1%(即1/(1+99))的垃圾收集时间。

-XX参数常见的参数还有一种缩写模式(虽然是-X开头,但是其实是-XX参数)

比如:

  • -Xms 等价于-XX:InitialHeapSize 表示初始化的堆大小
  • -Xmx 等价于-XX:MaxHeapSize 表示最大的堆的大小
  • -Xss 等价于 -XX:ThreadStackSize 线程堆栈的大小

二、JDK的命令行监控工具

查看JVM运行时参数的值

1、-XX:+PrintFlagsFina

1 ) -XX:+PrintFlagsFinal 打印出所有XX参数和值

java -XX: +PrintFlagsFinal

在这里插入图片描述
=表示默认值
:= 表示被用户或者JVM修改后的值

2、-XX:+PrintFlagsInitial

java -XX:+PrintFlagsInitial

打印的是XX参数的初始化值

这里列举一小部分:

在这里插入图片描述
3、-XX:+PrintCommandLineFlags

这个参数让JVM打印出那些已经被用户或者JVM设置过的详细的XX参数的名称和值。

它列举出 -XX:+PrintFlagsFinal的结果中第三列有”:=”的参数。

以这种方式,我们可以用-XX:+PrintCommandLineFlags作为快捷方式来查看修改过的参数

正常使用

java -XX:+PrintCommandLineFlags

可以查看到垃圾回收器的信息

别人测试的都能看到垃圾回收器的信息,我的JVM并不能看到这个信息,暂时没有找到原因,这里记录一下。下面两张图引自传送门

在这里插入图片描述
在这里插入图片描述

4、-XX:+UnlockDiagnosticVMOptions解锁诊断参数

5、-XX:+UnlockExperimentalVMOptions解锁实验参数

这两条后面会补上。

(1)jps

在介绍这几款工具之前,有必要说明:JDK内置命令行工具的监控的详细信息都可以在这里找到 传送门

jps是虚拟机进程状况工具

下面到了JDK的命令行监控工具的使用,首先是jps,使用非常简单。

Java的jps 是专门查看Java进程的
在这里插入图片描述
加一个 -l参数,可以看到完整的名称
在这里插入图片描述

jps工具主要选项
在这里插入图片描述

(2)jinfo

jinfo 是 Java配置信息工具

jinfo(Configuration Info for Java)的作用是实时查看和调整虚拟机各项参数。

使用jps命令的-v参数可以查看虚拟机启动时显式指定的参数列表,但如果想知道未被显式指定的参数的系统默认值,除了去找资料外,就只能使用jinfo的-flag选项进行查询了(如果只限于JDK 6或以上版本的话,使用java-XX:+PrintFlagsFinal查看参数默认值也是一个很好的选择)。

jinfo还可以使用-sysprops选项把虚拟机进程的System.getProperties()的内容打印出来。这个命令在JDK5时期已经随着Linux版的JDK发布,当时只提供了信息查询的功能,JDK 6之后,jinfo在Windows和Linux平台都有提供,并且加入了在运行期修改部分参数值的能力(可以使用-flag[+|-]name或者-flag name=value在运行期修改一部分运行期可写的虚拟机参数值)。在JDK 6中,jinfo对于Windows平台功能仍然有较大限制,只提供了最基本的-flag选项。

可以使用 jinfo -flag <参数名称> 命令行工具去查看当前java进程的JVM参数。

比如:

jinfo -flag MaxHeapSize pid

在这里插入图片描述
查看线程堆栈的大小 这里显示是1024KB。
在这里插入图片描述
查看是否使用了启用相关的配置。比如压缩指针。
在这里插入图片描述
+ 表示有
- 表示没有

在堆中,32位的对象引用(指针)占4个字节,而64位的对象引用占8个字节。也就是说,64位的对象引用大小是32位的2倍。64位JVM在支持更大堆的同时,由于对象引用变大却带来了性能问题。为了能够保持32位的性能,oop须保留32位。方法是压缩指针

(3)jstat

jstat是虚拟机统计信息监视工具

确切点来说,jstat(JVM Statistics Monitoring Tool)是用于监视虚拟机各种运行状态信息的命令行工具。它可以显示本地或者远程[插图]虚拟机进程中的类加载、内存、垃圾收集、即时编译等运行时数据,在没有GUI图形界面、只提供了纯文本控制台环境的服务器上,它将是运行期定位虚拟机性能问题的常用工具。

比如类装载信息、垃圾收集信息、以及JIT编译信息等

命令格式如下:

在这里插入图片描述
option主要有如下内容:

-class 查看类加载信息
-compiler 查看编译信息
-gc 查看垃圾回收信息

更多的选项,可以从-help查看,或者想要了解更多, JDK内置命令行工具的监控的详细信息都可以在这里找到 传送门

演示

java -class 进程号 间隔毫秒数 输出次数

在这里插入图片描述

-class option
Class loader statistics. 

Loaded: Number of classes loaded. -- 加载的类的个数
Bytes: Number of kBs loaded.  -- 加载的类的大小
Unloaded: Number of classes unloaded -- 卸载的类的个数.
Bytes: Number of Kbytes unloaded.  -- 卸载的类的大小
Time: Time spent performing class loading and unloading operations. -- 花在加载和卸载类的时间

垃圾收集的内容还是比较多的。

java -gc 进程号  间隔毫秒数 输出次数

在这里插入图片描述

参数比较多哈,但是根据下面的注释 很容易区分出JVM中的每一个分块。

其中S0C、S0U。C表示总容量大小,U表示使用大小,这样就好理解了。

  • S0C、S1C、S0U、S1U:S0和S1的总量与使用量
  • EC、EU:Eden区总量与使用量
  • OC、OU:Old区总量与使用量
  • MC、MU: Metaspace区总量与使用量
  • CCSC、CCSU:压缩类空间总量与使用量(压缩指针的作用)
  • YGC、YGCT:Young Go的次数与时间
  • FGC、FGCT:FullGC的次数与时间
  • GCT:总的GC时间
-gc option
Garbage-collected heap statistics.

S0C: Current survivor space 0 capacity (kB).  -- 
S1C: Current survivor space 1 capacity (kB).
S0U: Survivor space 0 utilization (kB). 
S1U: Survivor space 1 utilization (kB).
EC: Current eden space capacity (kB).
EU: Eden space utilization (kB).
OC: Current old space capacity (kB).
OU: Old space utilization (kB).
MC: Metaspace capacity (kB).
MU: Metacspace utilization (kB).
CCSC: Compressed class space capacity (kB).
CCSU: Compressed class space used (kB).
YGC: Number of young generation garbage collection events.
YGCT: Young generation garbage collection time.
FGC: Number of full GC events.
FGCT: Full garbage collection time.
GCT: Total garbage collection time.

除此之外 还有别的option。具体需要查文档,演示的内容是比较常用的。

下面有必要说一下Java的内存结构:

JVM主要分成两大块,一块是堆区、一块是非堆区。

堆区又分成两大块,一块是Young、一块是Old。

如果young区是复制算法的话(现在的商用Java虚拟机大多都优先采用了这种收集算法去回收新生代)。

Young区会被分成两大块,一块是S0+S1、另外一块是Eden,对象优先在Eden分配。

发生垃圾收集时,将Eden和Survivor中仍然存活的对象一次性复制到另外一块Survivor空间上,然后直接清理掉Eden和已用过的那块Survivor空间。

HotSpot虚拟机默认Eden和Survivor的大小比例是8∶1,也即每次新生代中可用内存空间为整个新生代容量的90%(Eden的80%加上一个Survivor的10%),只有一个Survivor空间,即10%的新生代是会被“浪费”的。
在这里插入图片描述
非堆区采用的是操作系统的本地内存,Java8中引入了元空间(方法区移入Metaspace),里面有两个重要的内容,一个是如果启用了压缩指针,就会存在CCS。CodeCahe中存放的是JIT的代码信息(Java代码转换成的native代码、JNI代码)。

下面看一下JIT编译的情况

java  -compiler 进程号
java -printcompilation 进程号

在这里插入图片描述

-compiler option
Java HotSpot VM Just-in-Time compiler statistics.

Compiled: Number of compilation tasks performed. -- 完成多少编译任务(将方法编译成本地代码)
Failed: Number of compilations tasks failed.  -- 失败的
Invalid: Number of compilation tasks that were invalidated. -- 错误的、无效的
Time: Time spent performing compilation tasks. -- 时间
FailedType: Compile type of the last failed compilation. 
FailedMethod: Class name and method of the last failed compilation.

在这里插入图片描述

Compiled: Number of compilation tasks performed by the most recently compiled method.
Size: Number of bytes of byte code of the most recently compiled method.
Type: Compilation type of the most recently compiled method.
Method: Class name and method name identifying the most recently compiled method. Class name uses slash (/) instead of dot (.) as a name space separator. Method name is the method within the specified class. The format for these two fields is consistent with the HotSpot -XX:+PrintCompilation option.

其它选项的说明:
在这里插入图片描述

(4)jmap

jmap是Java内存映像工具

jmap(Memory Map for Java)命令可以用于生成堆转储快照(一般称为heapdump或dump文件),还可以查询finalize执行队列、Java堆和方法区的详细信息,如空间使用率、当前用的是哪种收集器等。

和jinfo命令一样,jmap有部分功能在Windows平台下是受限的,除了生成堆转储快照的-dump选项和用于查看每个类的实例、空间占用统计的-histo选项在所有操作系统中都可以使用之外,其余选项都只能在Linux/Solaris中使用。

命令格式

jmap [option] vmid  进程号

option选项的合法值与具体含义如表示。
在这里插入图片描述

通过jmap可以定位内存溢出的问题。

我们看一下堆溢出

public class Main {

    public static void main(String[] args) {
        ArrayList list = new ArrayList();
        while (true) {
            list.add(new Main());
        }
    }
}

结果:java.lang.OutOfMemoryError

Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
	at java.base/java.util.Arrays.copyOf(Arrays.java:3721)
	at java.base/java.util.Arrays.copyOf(Arrays.java:3690)
	at java.base/java.util.ArrayList.grow(ArrayList.java:235)
	at java.base/java.util.ArrayList.grow(ArrayList.java:242)
	at java.base/java.util.ArrayList.add(ArrayList.java:452)
	at java.base/java.util.ArrayList.add(ArrayList.java:465)
	at com.leetcodePractise.test.Main.main(Main.java:13)

Process finished with exit code 1

再看一下非堆溢出,非堆指的是非堆内存,主要包括元空间 , Jvm Stack(java虚拟机栈), Local Method Statck(本地方法栈)。

public class Main {

    public static void main(String[] args) {
        new Main().test();
    }

    public void test() {
        test();
    }
}

结果:java.lang.StackOverflowError

Exception in thread "main" java.lang.StackOverflowError
	at com.leetcodePractise.test.Main.test(Main.java:10)
	at com.leetcodePractise.test.Main.test(Main.java:10)
	at com.leetcodePractise.test.Main.test(Main.java:10)
	at com.leetcodePractise.test.Main.test(Main.java:10)
	....

当发生内存溢出的时候,我们希望有内存映像文件的自动导出,来帮助我们去进行分析。这里有两种方式,一种是通过参数的设置

-XX:+HeapDumpOnOutOfMemoryError   意思是当发生内存溢出的时候将Heap dump出来
-XX:HeapDumpPath=./  意思是导出的文件存放在哪个路径下面

另外一种方式是使用jmap命令手动导出
在这里插入图片描述
dump文件直接打开是乱码的,如果我们要分析dump文件,需要借助工具MAT,可以参考这篇文章传送门

下面演示一下其他的option

-heap 显示每个区块占多大的内存。
在这里插入图片描述
其他的选项使用类型。

(5)jhat

jhat是虚拟机堆转储快照分析工具

JDK提供jhat(JVM Heap Analysis Tool)命令与jmap搭配使用,来分析jmap生成的堆转储快照。

jhat内置了一个微型的HTTP/Web服务器,生成堆转储快照的分析结果后,可以在浏览器中查看。

不过,在实际工作中,除非手上真的没有别的工具可用,否则多数人是不会直接使用jhat命令来分析堆转储快照文件的,主要原因有两个方面。

  1. 一般不会在部署应用程序的服务器上直接分析堆转储快照,即使可以这样做,也会尽量将堆转储快照文件复制到其他机器上进行分析,因为分析工作是一个耗时而且极为耗费硬件资源的过程,既然都要在其他机器上进行,就没有必要再受命令行工具的限制了。
  2. jhat的分析功能相对来说比较简陋,有大量工具都能实现比jhat更强大专业的分析功能。比如上面介绍的MAT。

(6)jstack

jstack是Java堆栈跟踪工具

jstack(Stack Trace for Java)命令用于生成虚拟机当前时刻的线程快照(一般称为threaddump或者javacore文件)。线程快照就是当前虚拟机内每一条线程正在执行的方法堆栈的集合,生成线程快照的目的通常是定位线程出现长时间停顿的原因,如线程间死锁、死循环、请求外部资源导致的长时间挂起等,都是导致线程长时间停顿的常见原因。线程出现停顿时通过jstack来查看各个线程的调用堆栈,就可以获知没有响应的线程到底在后台做些什么事情,或者等待着什么资源。

jstack命令格式:

jstack [option] 进程号

选项
在这里插入图片描述
演示
在这里插入图片描述
这是JIT负责编译的线程
在这里插入图片描述
线程状态是一项非常重要的信息,记牢了。
在这里插入图片描述

想必大家都曾遇到这样的问题:为什么有时中CPU占用会过高,怎么查看与解决?

关于CPU占用过高的原因大致有这几条:
1、大型程序:可能是编写的程序有不合理的地方,也有可能是电脑配置低
2、病毒与木马:病毒、木马造成。比如大量的蠕虫病毒在系统内部迅速复制,造成CPU占用资源率据高不下。
3、磁盘碎片:经常对文档进行复制和删除,会使得硬盘中数据排列非常分散,使计算机在查找的时候速度变慢,从而占用大量的CPU。

关于排查可以这样做:

通过top命令查看是哪个进程导致CPU占用率高 然后shitf+p 倒序排列

在这里插入图片描述
如果是Java进程引起的问题,我们就需要看一下Java进程中的详细信息了。

在终端执行 top -H -p ,查询pid进程下的所有线程消耗cpu的情况,然后shift+p倒序找到消耗cpu最高的线程id,如图:

在这里插入图片描述

日志文件中线程id是采用16进制的,所以要将查询出来的10进制线程id转换成16进制,数字转换的方式有很多中,在这里通过linux命令printf转换,如下:

在这里插入图片描述

使用jstack下载堆栈信息日志文件,sudo -u appadmin jstack 179258 >/usr/local/test2.txt,如下:

因为当前用户是root,而非179258进程所属用户appadmin,因此需要切换用户。然后根据进程id将对应的日志重定向到某个目录下,当然也可以在线查找 sudo -u appadmin jstack <进程id> | grep -a <16进制线程id>.

生成的日志文件位于linux上,可以通过sz下载到windows下进行查找,根据上文转成16进制的线程id进行全局搜索,就能定位到具体的原因,如图:

在这里插入图片描述
根据以上步骤能快速找到问题所在,但是这个日志文件的分析是需要经验积累的,只有当出现诸如线程死锁的情况下,会出现很明显的日志,deadLock关键字样,并且能提示出现问题的代码所在行。其他问题如网络带宽,文件io等硬件资源跟不上的原因导致,是需要有一定的经验积累才能发现的!

参考《深入理解Java虚拟机:JM高级特性与最佳实践》

发布了205 篇原创文章 · 获赞 3768 · 访问量 59万+

猜你喜欢

转载自blog.csdn.net/qq_42322103/article/details/105087128
今日推荐