JVM性能监控

笔记参考于尚硅谷宋红康:JVM全套教程:https://www.bilibili.com/video/BV1PJ411n7xZ

1.概述篇

1.问题

1.生产环境中的问题

  1. 生产环境发生了内存溢出该如何处理?
  2. 生产环境应该给服务器分配多少内存合适?
  3. 如何对垃圾回收器的性能进行调优?
  4. 生产环境CPU负载飙高该如何处理?
  5. 生产环境应该给应用分配多少线程合适?
  6. 不加log,如何确定请求是否执行了某一行代码?
  7. 不加log,如何实时查看某个方法的入参与返回值?

2.为什么要调优?

  1. 防止出现OOM
  2. 解决OOM
  3. 减少Full GC出现的频率

3.不同阶段的考虑

  1. 上线前
  2. 项目运行阶段
  3. 线上出现OOM

 

2.调优概述

1.监控的依据

  1. 运行日志
  2. 异常堆栈
  3. GC日志
  4. 线程快照
  5. 堆转储快照

2.调优的大方向

  1. 合理地编写代码
  2. 充分并合理的使用硬件资源
  3. 合理地进行VM调优

 

3.性能优化的步骤

第1步(发现问题):性能监控

  • GC 频繁
  • cpu load过高
  • OOM内存泄漏
  • 死锁
  • 程序响应时间较长

一种以非强行或者入侵方式收集或查看应用运营性能数据的活动。

监控通常是指一种在生产、质量评估或者开发环境下实施的带有预防或主动性的活动。

当应用相关干系人提出性能问题却没有提供足够多的线索时,首先我们需要进行性能监控,随后是性能分析。

 

第2步(排查问题):性能分析

  • 打印GC日志,通过GCviewer或者http://gceasy.io来分析日志信息
  • 灵活运用命令行工具,jstack, jmap, jinfo等
  • dump出堆文件,使用内存分析工具分析文件
  • 使用阿里Arthas,或jconsole, JVisualVM来实时查看JVM状态
  • jstack查看堆栈信息

一种以侵入方式收集运行性能数据的活动,它会影响应用的吞吐量或响应性。

性能分析是针对性能问题的答复结果,关注的范围通常比性能监控更加集中

性能分析很少在生产环境下进行,通常是在质量评估、系统测试或者开发环境下进行,是性能监控之后的步骤。

 

第3步(解决问题):性能调优

  • 适当增加内存,根据业务背景选择垃圾回收器
  • 优化代码,控制内存使用
  • 增加机器,分散节点压力
  • 合理设置线程池线程数量
  • 使用中间件提高程序效率,比如缓存,消息队列等
  • 其他

一种为改善应用响应性或吞吐量而更改参数、源代码、属性配置的活动,性能调优是在性能监控、性能分析之后的活动

 

4.性能评价/测试指标

 

1.停顿时间(或响应时间)

1.web应用角度:提交请求和返回该请求的响应之间使用的时间,一般比较关注平均响应时间

常用操作的响应时间列表:

2.在垃圾回收环节中:

暂停时间:执行垃圾收集时,程序的工作线程被暂停的时间。

-XX:MaxGCPauseMillis

 

2.吞吐量

  • 对单位时间内完成的工作量(请求)的量度
  • 在GC中:运行用户代码的时间占总运行时间的比例(总运行时间:程序的运行时间+内存回收的时间)吞吐量为1 – 1/(1+n)。 -XX:GCTimeRatio=n

 

3.并发数

同一时刻,对服务器有实际交互的请求数

1000个人同时在线,估计并发数在5%-15%之间,也就是同时并发量: 50-150之间。

4.内存占用

Java 堆区所占的内存大小

 

5.相互间的关系

以高速公路通行状况为例

吞吐量:每天通过高速公路收费站的车辆的数据(也可以理解为收费站收取的高速费)

并发数:高速公路上正在行驶的车辆的数目

响应时间:车速

 

高速公路上正在行驶的车辆的数目(并发数)少了,车速(响应时间)就可以快了,但收费站收取的高速费(吞吐量)不见的会高。

 

2.JVM监控及诊断工具-命令行篇

 

1.概述(简单命令行工具)

性能诊断是软件工程师在日常工作中需要经常面对和解决的问题,在用户体验至上的今天,解决好应用的性能问题能带来非常大的收益。

Java作为最流行的编程语言之一,其应用性能诊断一直受到业界广泛关注。可能造成Java应用出现性能问题的因素非常多,例如线程控制、磁盘读写、数据库访问、网络I/O、垃圾收集等。想要定位这些问题,一款优秀的性能诊断工具必不可少。

 

体会1:使用数据说明问题,使用知识分析问题,使用工具处理问题。

体会2:无监控、不调优!

 

简单命令行工具:

最先了解的两个命令就是javac. java,

我们进入到安装jdk的bin目录,发现还有一系列辅助工具,这些辅助工具用来获取目标JVM不同方面、不同层次的信息,帮助开发人员很好地解决Java应用程序的一些疑难杂症。

源码:

jdk/jdk11: 1ddf9a99e4ad /src/jdk.jcmd/share/classes/sun/tools/ (openjdk.org)

 

2.jps:查看正在运行的Java进程

 

基本情况:

jps(Java Process Status):

显示指定系统内所有的HotSpot虚拟机进程(查看虚拟机进程信息),可用于查询正在运行的虚拟机进程。

说明:对于本地虚拟机进程来说,进程的本地虚拟机ID与操作系统的进程ID是一致的,是唯一的。

 

测试:

package com.atguigu.jps;

import java.util.Scanner;

public class ScannerTest {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        String info = scanner.next();
    }
}

再执行一次jps,jps进程号会变,因为又启动了一次

 

基本语法:

它的基本使用语法为:jps [options] [hostid]

我们还可以通过追加参数,来打印额外的信息。

 

options参数:

  • -q :仅仅显示LVMID (local virtual machine id),即本地虚拟机唯一id.不显示主类的名称等
  • -l:输出应用程序主类的全类名或如果进程执行的是jar包,则输出jar完整路径
  • -m:输出虚拟机进程启动时传递给主类main()的参数
  • -v:列出虚拟机进程启动时的JVM参数。比如: -Xms20m -Xmx50m是启动程序指定的jvm参数。

jps -m:在idea传递给主类main()的参数

加入VM Option

-Xms20m -Xmx50m

 

 

补充:

如果某Java进程关闭了默认开启的UsePerfData参数(即使用参数-XX:-UsePerfData) ,那么jps命令(以及下面介绍的jstat)将无法探知该Java 进程。

 

 

说明:以上参数可以综合使用,但-q只能单独使用,否则只显示

 

hostid参数:

RMI注册表中注册的主机名。

如果想要远程监控主机上的java程序,需要安装jstatd.

对于具有更严格的安全实践的网络场所而言,可能使用一个自定义的策略文件来显示对特定的可信主机或网络的访问,尽管这种技术容易受到IP地址欺诈攻击。

如果安全问题无法使用一个定制的策略文件来处理,那么最安全的操作是不运行jstatd服务器,而是在本地使用jstat和jps工具。

 

 

3.jstat:查看JVM统计信息

 

1.基本情况

jstat(JVM Statistics Monitoring Tool):用于监视虚拟机各种运行状态信息的命令行工具。它可以显示本地或者远程虚拟机进程中的类装载、内存、垃圾收集、JIT编译等运行数据。

在没有GUI图形界面,只提供了纯文本控制台环境的服务器上,它将是运行期定位虚拟机性能问题的首选工具。常用于检测垃圾回收问题以及内存泄漏问题。

官方文档:

jstat (oracle.com)

 

基本语法

它的基本使用语法为:

jstat -<option> [-t] [-h<lines>] <vmid> [<interval> [<count>]]

查看命令相关参数:jstat -h 或 jstat -help

 

option参数:

测试代码:

package com.yutian.jps;

import java.util.Scanner;

public class ScannerTest {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        String info = scanner.next();
    }
}

选项option可以由以下值构成。

类装载相关的:

  • -class:显示ClassLoader的相关信息:类的装载、卸载数量、总空间、类装载所消耗的时间等

 

垃圾回收相关的:

 

package com.yutian.jstat;

import java.util.ArrayList;


public class GCTest {
    public static void main(String[] args) {
        ArrayList<byte[]> list = new ArrayList<>();

        for (int i = 0; i < 1000; i++) {
            byte[] arr = new byte[1024 * 100];//100KB
            list.add(arr);
            try {
                Thread.sleep(120);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}
加入VM Option
-Xms60m -Xmx60m -XX:SurvivorRatio=8
-gc:显示与GC相关的堆信息。包括Eden区、两个Survivor区、老年代、永久代等的容量、已用空间、GC时间合计等信息。

新生代相关:

  • 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的总时间(YGCT和FGCT的和)

 

 

-gcutil:显示内容与-gc基本相同,但输出主要关注已使用空间占总空间的百分比。

 

剩下的自己可以演示一下:

-gccapacity:显示内容与-gc基本相同,但输出主要关注Java堆各个区域使用到的最大、最小空间。

-gccause:与-gcutil功能一样,但是会额外输出导致最后一次或当前正在发生的GC产生的原因。

-gcnew:显示新生代GC状况

-gcnewcapacity:显示内容与-gcnew基本相同,输出主要关注使用至的最大、最小空间

-geold: 显示老年代GC状况

 

JIT相关的:

  • -compiler:显示JIT编译器编译过的方法、耗时等信息
  • -printcompilation:输出已经被JIT编译的方法

 

 

 

 

测试代码:

package com.yutian.jps;

import java.util.Scanner;

public class ScannerTest {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        String info = scanner.next();
    }
}

 

interval参数:

jstat -<option> [-t] [-h<lines>] <vmid> [<interval> [<count>]]

interval参数用于指定输出统计数据的周期,单位为毫秒。即:查询间隔

如下图:每隔一秒打印一次

 

count参数:

count参数用于指定查询的总次数,如果没有指定count参数会一直打印下去

 

注意:count参数和interval参数,只写一个默认为interval参数

 

 

-t参数:

jstat -<option> [-t] [-h<lines>] <vmid> [<interval> [<count>]]

可以在输出信息前加上一个Timestamp列,显示程序的运行时间(即运行到现在的时间)。单位:秒

注意:严格按照顺序[-t]放在-<option>后,在其他前

 

经验:

我们可以比较Java进程的启动时间(Timestamp列)以及总GC时间(GCT列) ,或者两次测量的间隔时间以及总GC时间的增量,来得出GC时间占运行时间的比例。如果该比例超过20%,则说明目前堆的压力较大;如果该比例超过90%,则说明堆里几乎没有可用空间,随时都可能抛出OOM 异常。

 

-h参数:

jstat -<option> [-t] [-h<lines>] <vmid> [<interval> [<count>]]

可以在周期性数据输出时,输出多少行数据后输出一个表头信息

 

 

补充:

jstat还可以用来判断是否出现内存泄漏。

第1步:在长时间运行的Java程序中,我们可以运行jstat命令连续获取多行性能数据,并取这几行数据中ou列(即已占用的老年代内存)的最小值(选择一组中的最小值,避免出现奇异值)。

第2步:然后,我们每隔一段较长的时间重复一次上述操作,来获得多组ou最小值。如果这些值呈上涨趋势,则说明该Java程序的老年代内存已使用量在不断上涨,这意味着无法回收的对象在不断增加,因此很有可能存在内存泄漏。

 

 

4.jinfo:实时查看和修改JVM配置参数

 

基本情况

jinfo(Configuration Info for Java)查看虚拟机配置参数信息也可用于调整虚拟机的配置参数。

在很多情况下, Java应用程序不会指定所有的Java虚拟机参数,而此时,开发人员可能不知道某一个具体的Java虚拟机参数的默认值。在这种情况下,可能需要通过查找文档获取某个参数的默认值,这个查找过程可能是非常艰难的。但有了jinfo工具,开发人员可以很方便地找到Java虚拟机参数的当前值。

官方文档:

jinfo (oracle.com)

 

 

 

基本语法

它的基本使用语法为:

jinfo [options] pid

说明:java 进程ID 必须要加上

 

查看:

我们继续使用之前写过的代码ScannerTest

 

jinfo -sysprops PID:

可以查看由System.getProperties()取得的参数

 

jinfo -flags PID:

查看曾经赋过值的一些参数(注意flags有s)

加入VM option

-Xms60m -Xmx60m

 

jinfo -flag 具体参数 PID:

查看某个java进程的具体参数(注意flag没有s)

 

 

修改:

jinfo不仅可以查看运行时某一个Java虚拟机参数的实际取值,甚至可以在运行时修改部分参数,并使之立即生效。

但是,并非所有参数都支持动态修改。参数只有被标记为manageable的flag可以被实时修改。其实,这个修改能力是极其有限的。

可以查看被标记为manageable的参数(linux):

java -XX:+PrintFlagsFinal -version | grep manageable

是的,没错,只有这一点

 

针对boolean类型:jinfo -flag [+|-]具体参数 PID

 

针对非boolean类型:jinfo -flag 具体参数=具体参数值PID

拓展

  • java-XX:+PrintFlagsInitial  :  查看所有JVM参数启动的初始值
  • java-XX:+PrintFlagsFinal    :   查看所有JVM参数的最终值
  • java-XX:+PrintCommandLineFlags    : 查看那些已经被用户或者JVM设置过的详细的XX参数的名称和值

 

 

5.jmap:导出内存映像文件&內存使用情况

 

基本情况

jmap(JVM Memory Map):作用一方面是获取dump文件(堆转储快照文件,二进制文件),它还可以获取目标Java进程的内存相关信息,包括Java堆各区域的使用情况、堆中对象的统计信息、类加载信息等。

开发人员可以在控制台中输入命令“jmap -help”查阅jmap工具的具体使用方式和一些标准选项配置。

官方帮助文档:jmap (oracle.com)

 

 

基本语法

它的基本使用语法为:

  • jmap [option] <pid>
  • jmap [option] <executable <core>
  • jmap [option] [server_id@]<remote server IP or hostname>

其中option包括

说明:这些参数和linux下输入显示的命令多少会有不同,包括也受jdk版本的影响。

 

 

-dump

生成Java堆转储快照:dump文件

特别的: -dump:live只保存堆中的存活对象

 

-heap

输出整个堆空间的详细信息,包括GC的使用、堆配置信息,以及内存的使用信息等

 

-histo

输出堆中对象的统计信息,包括类、实例数量和合计容量

特别的:-histo:live只统计堆中的存活对象

 

-permstat

以ClassLoader为统计口径输出永久代的内存状态信息

仅linux/solaris平台有效

 

-finalizerinfo

显示在F-Queue中等待Finalizer线程执行finalize方法的对象

仅linux/solaris平台有效

 

-F

当虚拟机进程对-dump选项没有任何响应时,可使用此选项强制执行生成dump文件

仅linux/solaris平台有效

 

-h |-help

jmap工具使用的帮助命令

 

-J <flag>

传递参数给jmap启动的jvm

 

使用1:导出内存映像文件

一般来说,使用jmap指令生成dump文件的操作算得上是最常用的jmap命令之一,将堆中所有存活对象导出至一个文件之中。

Heap Dump又叫做堆存储文件,指一个Java进程在某个时间点的内存快照。Heap Dump在触发内存快照的时候会保存此刻的信息如下:

 

  • All Objects

Class,fields,primitive values and references

  • All Classes

ClassLoader,name,super class,static fields

  • Garbage Collectiop Roots

Objects defined to be reachable by the JVM

  • Thread Stacks and Local Variables

The call-stacks of threads at the moment of the snapshot,and per-frame information about local objects

 

说明:

1.通常在写Heap Dump文件前会触发一次Full GC,所以heap dump文件里保存的都是Full GC后留下的对象信息。

2,由于生成dump文件比较耗时,因此大家需要耐心等待,尤其是大内存镜像生成dump文件,则需要耗费更长的时间来完成。

package com.yutian.jmap;

import java.util.ArrayList;

public class GCTest {
    public static void main(String[] args) {
        ArrayList<byte[]> list = new ArrayList<>();

        for (int i = 0; i < 1000; i++) {
            byte[] arr = new byte[1024 * 100];//100KB
            list.add(arr);
            try {
                Thread.sleep(60);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}
手动的方式

注意:要加format才能与hprof格式匹配

jmap -dump:format-b,file=<filename.hprof> <pid>

jmap -dump:live,format=b,file=<filename.hprof> <pid>

如果安装了JProfiler,就是以下图标:

可以看到文件越来越大。4.hprop是使用:live生成的,在其他程序可能会比其他小。

 

 

自动的方式

当程序发生OOM退出系统时,一些瞬时信息都随着程序的终止而消失,而重现OOM问题往往比较困难或者耗时。此时若能在OOM时,自动导出dump文件就显得非常迫切。

这里介绍一种比较常用的取得堆快照文件的方法,即使用:

-XX:+HeapDumpOnOutOfMemoryError:在程序发生OOM时,导出应用程序的当前堆快照。

-XX:HeapDumpPath:可以指定堆快照的保存位置。

 

比如:

-Xmx100m -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=d:\JPO\m.hprof

 

使用2:显示堆内存相关信息

jmap -heap pid

 

 

jmap -histo pid

显示占用情况:

 

 

 

使用3:其它作用:

jmap -permstat pid查看系統的ClassLoader信息

jmap -finalizerinfo查看堆积在finalizer队列中的对象

 

 

小结

由于jmap将访问堆中的所有对象,为了保证在此过程中不被应用线程干扰, jmap需要借助安全点机制,让所有线程停留在不改变堆中数据的状态。也就是说,由jmap导出的堆快照必定是安全点位置的。这可能导致基于该堆快照的分析结果存在偏差。

举个例子,假设在编译生成的机器码中,某些对象的生命周期在两个安全点之间,那么:live选项将无法探知到这些对象。

另外,如果某个线程长时间无法跑到安全点, jmap将一直等下去。与前面讲的jstat则不同,垃圾回收器会主动将jstat所需要的摘要数据保存至固定位置之中,而jstat只需直接读取即可。

 

 

6.jhat: JDK自带堆分析工具

基本情况

jhat(JVM Heap Analysis Tool):

Sun JDK提供的jhat命令与jmap命令搭配使用,用于分析jmap生成的heap dump文件(堆转储快照) 。jhat内置了一个微型的HTTP/HTML服务器, 生成dump文件的分析结果后, 用户可以在浏览器中查看分析结果(分析虚拟机转储快照信息)。

使用了jhat命令,就启动了一个http服务,端口是7000,即http://localhost:7000/,就可以在浏览器里分析。

说明: jhat命令在JDK9、JDK10中已经被删除,官方建议用VisualVM代替。

 

 

 

 

基本语法

jhat [-stack <bool>] [-refs <bool>] [-port <port>] [-baseline <file>] [-debug <int>] [-version] [-h|-help] <file>

 

option参数: -stack false | true       关闭|打开对象分配调用栈跟踪

option参数: -refs false | true            关闭|打开对象引用跟踪

option参数: -port port -number             设置jhat HTTP Server的端口号,默认7000

option参数: -exclude exclude -file        执行对象查询时需要排除的数据成员

option参数: -baseline exclude -file          指定一个基准堆转储

option参数:-debug int        设置debug级别

option参数:-version         启动后显示版本信息就退出

option参数:-J<flag>            传入启动参数,比如-J -Xmx512m

 

 

 

7.jstack: 打印JVM中线程快照

 

基本情况:

jstack(JVM Stack Trace):用于生成虚拟机指定进程当前时刻的线程快照(虚拟机堆栈跟踪)。线程快照就是当前虚拟机内指定进程的每一条线程正在执行的方法堆栈的集合。

生成线程快照的作用:可用于定位线程出现长时间停顿的原因,如线程间死锁、死循环、请求外部资源导致的长时间等待等问题。

这些都是导致线程长时间停顿的常见原因。当线程出现停顿时,就可以用jstack显示各个线程调用的堆栈情况。

官方帮助文档:jstack (oracle.com)

在thread dump中,要留意下面几种状态

  • 死锁,Deadlock(重点关注)
  • 等待资源, Waiting on condition (重点关注)
  • 等待获取监视器, Waiting on monitor entry (重点关注)
  • 阻塞,Blocked(重点关注)
  • 执行中,Runnable
  • 暂停,Suspended
  • 对象等待中, Object.wait()或TIMED_WAITING
  • 停止,Parked

 

基本语法:

它的基本使用语法为:

jstack option pid

jstack管理远程进程的话,需要在远程程序的启动参数中增加:

  • -Djava.rmi.server.hostname=..
  • -Dcom.sun.management.jmxremote
  • -Dcom.sun.management.jmxremote.port=8888
  • -Dcom.sun.management.jmxremote.authenticate=false
  • -Dcom.sun.management.jmxremote.ssl=false

 

 

看一个死锁问题:

package com.yutian.jstack;

import java.util.Map;
import java.util.Set;

/**
* 演示线程的死锁问题
*/
public class ThreadDeadLock {

    public static void main(String[] args) {

        StringBuilder s1 = new StringBuilder();
        StringBuilder s2 = new StringBuilder();

        new Thread(){
            @Override
            public void run() {

                synchronized (s1){

                    s1.append("a");
                    s2.append("1");

                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }

                    synchronized (s2){
                        s1.append("b");
                        s2.append("2");

                        System.out.println(s1);
                        System.out.println(s2);
                    }

                }

            }
        }.start();


        new Thread(new Runnable() {
            @Override
            public void run() {
                synchronized (s2){

                    s1.append("c");
                    s2.append("3");

                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }

                    synchronized (s1){
                        s1.append("d");
                        s2.append("4");

                        System.out.println(s1);
                        System.out.println(s2);
                    }
                }
            }
        }).start();
    }


}
而可以看到是thread-1和thread-0出现堵塞

原因:thread-1和thread-0出现死锁

 

package com.yutian.jstack;

/**
* 演示线程:TIMED_WAITING
*/
public class TreadSleepTest {
    public static void main(String[] args) {
        System.out.println("hello - 1");
        try {
            Thread.sleep(1000 * 60 * 10);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println("hello - 2");
    }
}

 

 

option参数:-F : 当正常输出的请求不被响应时,强制输出线程堆栈

option参数:-l :  除堆栈外,显示关于锁的附加信息

option参数:-m  : 如果调用到本地方法的话,可以显示C/C++的堆栈

option参数:-h : 帮助操作

 

 

如果不使用jstack,使用代码也行:

package com.yutian.jstack;

import java.util.Map;
import java.util.Set;

/**
* 演示线程的死锁问题
*/
public class ThreadDeadLock {

    public static void main(String[] args) {

        StringBuilder s1 = new StringBuilder();
        StringBuilder s2 = new StringBuilder();

        new Thread(){
            @Override
            public void run() {

                synchronized (s1){

                    s1.append("a");
                    s2.append("1");

                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }

                    synchronized (s2){
                        s1.append("b");
                        s2.append("2");

                        System.out.println(s1);
                        System.out.println(s2);
                    }

                }

            }
        }.start();


        new Thread(new Runnable() {
            @Override
            public void run() {
                synchronized (s2){

                    s1.append("c");
                    s2.append("3");

                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }

                    synchronized (s1){
                        s1.append("d");
                        s2.append("4");

                        System.out.println(s1);
                        System.out.println(s2);
                    }
                }
            }
        }).start();

        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        new Thread(new Runnable() {
            @Override
            public void run() {
                Map<Thread, StackTraceElement[]> all = Thread.getAllStackTraces();//追踪当前进程中的所有的线程
                Set<Map.Entry<Thread, StackTraceElement[]>> entries = all.entrySet();
                for(Map.Entry<Thread, StackTraceElement[]> en : entries){
                    Thread t = en.getKey();
                    StackTraceElement[] v = en.getValue();
                    System.out.println("【Thread name is :" + t.getName() + "】");
                    for(StackTraceElement s : v){
                        System.out.println("\t" + s.toString());
                    }
                }
            }
        }).start();
    }


}

 

 

 

 

8.jcmd:多功能命令行

 

基本情况

在JDK 1.7以后,新增了一个命令行工具jcmd。

它是一个多功能的工具,可以用来实现前面除了jstat之外所有命令的功能。比如:用它来导出堆、内存使用、查看Java进程、导出线程信息、执行GC、JVM运行时间等。

官方帮助文档:https://docs.oracle.com/en/java/javase/11/tools/jcmd.html

jcmd拥有jmap的大部分功能,并且在Oracle的官方网站上也推荐使用jcmd命令代jmap命令

 

 

基本语法:

  • jcmd -l         列出所有的JVM进程
  • jcmd pid help     针对指定的进程,列出支持的所有命令
  • jcmd pid 具体命令            显示指定进程(jcmd pid help出来的指令)的指令命令的数据

 

 

9.jstatd: 远程主机信息收集

之前的指令只涉及到监控本机的Java应用程序,而在这些工具中,一些监控工具也支持对远程计算机的监控(如jps、jstat) 。为了启用远程监控,则需要配合使用jstatd工具。

命令jstatd是一个RMI服务端程序,它的作用相当于代理服务器,建立本地计算机与远程监控工具的通信。jstatd服务器将本机的Java应用程序信息传递到远程计算机。

 

 

参考资料:

尚硅谷宋红康:JVM全套教程:https://www.bilibili.com/video/BV1PJ411n7xZ

周志明:深入理解java虚拟机

Java虚拟机规范:Chapter 4. The class File Format (oracle.com)

[性能监控与分析] Java的浅堆和深堆_shangshanzixu的博客-CSDN博客

暂无评论

发送评论 编辑评论

|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇