一、内存监控背景

在做JVM内存分析前,需要堆JVM内存及垃圾回收算法和垃圾回收器有一定了解,具体可以参考我之前的一篇文章:常见的垃圾回收器及垃圾回收算法

1.1、为什么要做内存监控

我们在做开发的时候不可避免的会遇到一些问题,诸如下面这些问题:

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

做内存监控可以帮助我尽可能避免这些问题,做内存分析可以帮我们更快的定位问题所在,不论如何,结果都是我们要达到尽量减少问题、减少程序的相应时间、增加服务的处理速度和吞吐量,给用户带来更好的体验。

1.2、调优概述

我们一直说调优调优,调优调的是什么?既然是调优我们是从JVM层面考虑,就不需要考虑代码层面诸如死锁之类的东西,那么具体怎么调优呢?我们可以按照以下步骤来:

  1. 选择合适的机器。在运行项目时,首先要考虑项目的特性去选择合适的机器,这一步主要是考虑机器的类型、CPU及内存等硬件设施;
  2. 根据项目特点选择适合的垃圾回收器及算法。每种垃圾回收器和算法都有各自的优缺点,没有绝对完美的垃圾回收器和算法,我们要依据项目的侧重点去选择合适的垃圾回收器和算法,比如项目时侧重吞吐量还是稳定性或者处理速度;
  3. 根据项目的特点选择合适的GC属性。在选择完垃圾回收器之后,我们需要根据项目大小去设置合适的GC属性,比如最大堆内存、最小堆内存、年轻代大小及回收触发机制、老年代大小及回收触发机制、永久代大小等参数;
  4. 选择其他参数。设置完GC相关信息之后就可以考虑一些其他信息了,诸如是否打印日志、日志是否写入文件等。

我们要知道做JVM调优目的主要是防止出现和解决OOM、让JVM做出合理的GC。

1.3、调优的依据

调优的结果不一定是好的,所以我们可以根据一些信息去判断调优的结果:

  • 运行时日志
  • 异常堆栈
  • GC日志
  • 线程快照
  • 堆转储快照

1.4、程序性能优化的过程

1.4.1、性能监控

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

1.4.2、性能分析

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

1.4.3、性能调优

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

1.5、程序性能评价

调优的结果我们可以从以下几个方法去评价感知:

  • 请求时间:用户请求时消耗(等待)时间;
  • 停顿时间:GC时最大STW时间;
  • 并发量:同一时间对服务器有实际交互的请求数;
  • 吞吐量:运行用户代码的时间占总运行时间的比例(总运行时间:程序的运行时间+内存回收的时间);
  • 内存占用:JVM占用系统内存。

提交请求和返回该请求的响应之间使用的时间,一般比较关注平均响应时间。常用操作的响应时间列表:

操作 响应时间
打开一个站点 几秒
数据库查询一条记录(有索引) 十几毫秒
机械磁盘一次寻址定位 4毫秒
从机械磁盘顺序读取1M数据 2毫秒
从SSD磁盘顺序读取1M数据 0.3毫秒
从远程分布式换成Redis 读取一个数据 0.5毫秒
从内存读取 1M数据 十几微妙
Java程序本地方法调用 几微妙
网络传输2Kb数据 1 微妙

二、常用的命令

2.1、top:监控linux系统状况命令

top命令经常用来监控linux的系统状况,是常用的性能分析工具,能够实时显示系统中各个进程的资源占用情况。

top的使用方式 top [选项]

详细参数如下:

参数 说明
-b 以批处理模式操作
-c 显示完整的命令
-d 屏幕刷新间隔时间,默认5秒
-I 忽略失效过程
-s 保密模式
-S 累积模式
-i <时间> 设置刷新间隔时间
-u <用户名> 指定用户名
-p <进程号> 指定进程
-n <次数> 循环显示的次数

结果展示如下:

2.1.1、结果页前五行信息说明

第一行 15:19:43 up 2 days, 23:17,  0 users,  load average: 0.64, 0.62, 0.70

内容 含义
15:19:43 表示当前时间
up 2 days, 23:17 系统已运行时间
0 users 当前登录用户数
load average: 0.64, 0.62, 0.70 系统负载,即任务队列的平均长度。 三个数值分别为 1分钟、5分钟、15分钟前到现在的平均值。

需要注意的是: 如果load average这个数值除以逻辑CPU的数量,结果高于5的时候就表明系统在超负荷运转了。

第二行 Tasks:  34 total,   1 running,  33 sleeping,   0 stopped,   0 zombie

第三行 %Cpu(s):  0.0 us,  0.0 sy,  0.0 ni, 99.9 id,  0.0 wa,  0.0 hi,  0.0 si,  0.0 st

内容 含义
34 total 进程总数
1 running 正在运行的进程数
33 sleeping 睡眠的进程数
0 stopped 停止的进程数
0 zombie 僵尸进程数
0.0 us 用户空间占用CPU百分比
0.0 sy 内核空间占用CPU百分比
0.0 ni 用户进程空间内改变过优先级的进程占用CPU百分比
99.9 id 空闲CPU百分比
0.0 wa 等待输入输出的CPU时间百分比
0.0 hi 硬中断(Hardware IRQ)占用CPU的百分比
0.0 si 软中断(Software Interrupts)占用CPU的百分比
0.0 st 用于有虚拟cpu的情况,用来指示被虚拟机偷掉的cpu时间。

第四行 KiB Mem :  4194304 total,   793660 free,  2795212 used,   605432 buff/cache

第五行 KiB Swap:        0 total,        0 free,        0 used.   793660 avail Mem

内容 含义
4194304 total 物理内存总量
793660 free 空闲内存总量
2795212 used 使用的物理内存总量
605432 buff/cache 用作内核缓存的内存量
0 total 交换区总量
0 free 空闲交换区总量
0 used 使用的交换区总量
793660 avail Mem 代表可用于进程下一次分配的物理内存数量

2.1.2、结果页进程行说明

列名 含义
PID 进程id
PPID 父进程id
RUSER 用户名
UID 进程所有者的用户id
USER 进程所有者的用户名
GROUP 进程所有者的组名
TTY 启动进程的终端名。不是从终端启动的进程则显示为
PR 优先级
NI nice值。负值表示高优先级,正值表示低优先级
P 最后使用的CPU,仅在多CPU环境下有意义
%CPU 上次更新到现在的CPU时间占用百分比
TIME 进程使用的CPU时间总计,单位秒
TIME+ 进程使用的CPU时间总计,单位1/100秒
%MEM 进程使用的物理内存百分比
VIRT 进程使用的虚拟内存总量,单位kb。VIRT=SWAP+RES
SWAP 进程使用的虚拟内存中,被换出的大小,单位kb
RES 进程使用的、未被换出的物理内存大小,单位kb。RES=CODE+DATA
CODE 可执行代码占用的物理内存大小,单位kb
DATA 可执行代码以外的部分(数据段+栈)占用的物理内存大小,单位kb
SHR 共享内存大小,单位kb
nFLT 页面错误次数
nDRT 最后一次写入到现在,被修改过的页面数。
S 进程状态。D=不可中断的睡眠状态
COMMAND 命令名/命令行
WCHAN 若该进程在睡眠,则显示睡眠中的系统函数名
Flags 任务标志

2.1.3、结果页快捷键补充

在使用top命令查看linux的系统使用情况之后,我们可以在结果页面使用一些命令帮助我们更加方便的收集查看自己想要信息,具体命令如下:

参数 含义
数字1 监控每个逻辑CPU状况
b 打开关闭加亮效果
y 打开关闭加亮效果
x 打开关闭排序列的加亮效果,按shift+>或者shift+<改变排序序列
f 进入编辑基本视图中的显示字段页面。上下键选择选项,空格键决定是否在基本视图中显示这个选项,s保存更新,q或Esc退出

2.2、jps:查看正在运行的java进程

官网文档:jps - Java Virtual Machine Process Status Tool

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

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

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

options参数

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

注意:

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

hostid参数

RMI注册表中注册的主机名。如果想要远程监控主机上的 java 程序,需要安装 jstatd。

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

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

2.2.1、jps探知不到java进程信息

java程序启动后,默认(请注意是默认)会在/tmp/hsperfdata_userName目录下以该进程的id为文件名新建文件,并在该文件中存储jvm运行的相关信息,其中的userName为当前的用户名,
/tmp/hsperfdata_userName目录会存放该用户全部已经启动的java进程信息。
对于windows机器/tmp用Windows存放临时文件目录代替。

而jps、jconsole、jvisualvm等工具的数据来源就是这个文件(/tmp/hsperfdata_userName/pid)。
因此当该文件不存在或是没法读取时就会出现jps没法查看该进程号,jconsole没法监控等问题
或者hsperfdata_hdfs目录文件权限是777,修改成755,从新启动Java程序便可。

如果文件全部者和文件所属用户组与启动进程的用户不一致的话,在进程启动以后,就没有权限写/tmp/hsperfdata_username,因此/tmp/hsperfdata_username是一个空文件,理所固然jps也就没有任何显示。

2.3、jstat:显示JVM内存信息

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

官方文档:https://docs.oracle.com/javase/8/docs/technotes/tools/unix/jstat.html

基本使用语法为:jstat -<option> [-t] [-h<lines>] <vmid> [<interval> [<count>]]

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

option参数如下:

参数 说明
-class 显示ClassLoader的相关信息:类的装载、卸载数量、总空间、类装载所消耗的时间等
-gc 显示与GC相关的堆信息。包括Eden区、两个Survivor区、老年代、永久代等的容量、已用空间、GC时间合计等信息。
-gccapacity 显示内容与-gc基本相同,但输出主要关注Java堆各个区域使用到的最大、最小空间。
-gcutil 显示内容与-gc基本相同,但输出主要关注已使用空间占总空间的百分比。
-gccause 与-gcutil功能一样,但是会额外输出导致最后一次或当前正在发生的GC产生的原因。
-gcnew 显示新生代GC状况
-gcnewcapacity 显示内容与-gcnew基本相同,输出主要关注使用到的最大、最小空间
-geold 显示老年代GC状况
-gcoldcapacity 显示内容与-gcold基本相同,输出主要关注使用到的最大、最小空间
-gcpermcapacity 显示永久代使用到的最大、最小空间,jdk7使用。
-gcmetacapacity 显示元空间使用到的最大、最小空间,jdk8使用。
-compiler 显示JIT编译器编译过的方法、耗时等信息
-printcompilation 输出已经被JIT编译的方法

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

count参数: 用于指定查询的总次数

-t参数: 可以在输出信息前加上一个Timestamp列,显示程序的运行时间。单位:秒

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

2.3.1、jstat -class:类加载统计

结果说明:

属性 说明
Loaded 加载class的数量
Bytes 所占用空间大小
Unloaded 未加载数量
Bytes 未加载占用空间
Time 时间

2.3.2、jstat -compiler:编译统计

结果说明:

属性 含义
Compiled 编译数量
Failed 失败数量
Invalid 不可用数量
Time 时间
FailedType 失败类型
FailedMethod 失败的方法

2.3.3、jstat -gc:垃圾回收统计

结果说明:

属性 说明
EC Eden区的大小
EU Eden区已使用的大小
S0C 幸存者0区的大小
S1C 幸存者1区的大小
S0U 幸存者0区已使用的大小
S1U 幸存者1区已使用的大小
MC 元空间的大小
MU 元空间已使用的大小
OC 老年代的大小
OU 老年代已使用的大小
CCSC 压缩类空间的大小
CCSU 压缩类空间已使用的大小
YGC 从应用程序启动到采样时young
YGCT 从应用程序启动到采样时young
FGC 从应用程序启动到采样时full
FGCT 从应用程序启动到采样时的full
GCT 从应用程序启动到采样时gc的总时间

注意:这些输出对象的单位为KB。

2.3.4、jstat -gccapacity:堆内存统计

结果说明:

内容 含义
NGCMN 新生代最小容量
NGCMX 新生代最大容量
NGC 当前新生代容量
S0C 第一个幸存区大小
S1C 第二个幸存区的大小
EC 伊甸园区的大小
OGCMN 老年代最小容量
OGCMX 老年代最大容量
OGC 当前老年代大小
OC 当前老年代大小
MCMN 最小元数据容量
MCMX 最大元数据容量
MC 当前元数据空间大小
CCSMN 最小压缩类空间大小
CCSMX 最大压缩类空间大小
CCSC 当前压缩类空间大小
YGC 年轻代gc次数
FGC 老年代GC次数

2.3.5、jstat -gcnew:新生代垃圾回收统计

结果说明:

内容 含义
S0C 第一个幸存区大小
S1C 第二个幸存区的大小
S0U 第一个幸存区的使用大小
S1U 第二个幸存区的使用大小
TT 对象在新生代存活的次数
MTT 对象在新生代存活的最大次数
DSS 期望的幸存区大小
EC 伊甸园区的大小
EU 伊甸园区的使用大小
YGC 年轻代垃圾回收次数
YGCT 年轻代垃圾回收消耗时间

2.3.6、jstat -gcnewcapacity:新生代内存统计

结果说明:

内容 含义
NGCMN 新生代最小容量
NGCMX 新生代最大容量
NGC 当前新生代容量
S0CMX 最大幸存1区大小
S0C 当前幸存1区大小
S1CMX 最大幸存2区大小
S1C 当前幸存2区大小
ECMX 最大伊甸园区大小
EC 当前伊甸园区大小
YGC 年轻代垃圾回收次数
FGC 老年代回收次数

2.3.7、jstat -gcold:老年代垃圾回收统计

结果说明:

内容 含义
MC 方法区大小
MU 方法区使用大小
CCSC 压缩类空间大小
CCSU 压缩类空间使用大小
OC 老年代大小
OU 老年代使用大小
YGC 年轻代垃圾回收次数
FGC 老年代垃圾回收次数
FGCT 老年代垃圾回收消耗时间
GCT 垃圾回收消耗总时间

2.3.8、jstat -gcoldcapacity:老年代内存统计

结果说明:

内容 含义
OGCMN 老年代最小容量
OGCMX 老年代最大容量
OGC 当前老年代大小
OC 老年代大小
YGC 年轻代垃圾回收次数
FGC 老年代垃圾回收次数
FGCT 老年代垃圾回收消耗时间
GCT 垃圾回收消耗总时间

2.3.9、jstat -gcmetacapacity:元空间内存统计

结果说明:

内容 含义
MCMN 最小元数据容量
MCMX 最大元数据容量
MC 当前元数据空间大小
CCSMN 最小压缩类空间大小
CCSMX 最大压缩类空间大小
CCSC 当前压缩类空间大小
YGC 年轻代垃圾回收次数
FGC 老年代垃圾回收次数
FGCT 老年代垃圾回收消耗时间
GCT 垃圾回收消耗总时间

2.3.10、jstat -gcutil:总结垃圾回收统计

结果说明:

内容 含义
S0 幸存1区当前使用比例
S1 幸存2区当前使用比例
E 伊甸园区使用比例
O 老年代使用比例
M 元数据区使用比例
CCS 压缩使用比例
YGC 年轻代垃圾回收次数
FGC 老年代垃圾回收次数
FGCT 老年代垃圾回收消耗时间
GCT 垃圾回收消耗总时间

2.3.11、jstat -printcompilation:JVM编译方法统计

结果说明:

内容 含义
Compiled 最近编译方法的数量
Size 最近编译方法的字节码数量
Type 最近编译方法的编译类型
Method 方法名标识

2.3.1、jstat判断内存泄漏

jstat是用来监控JVM运行信息的,基于此信息,我们可以用jstat命令来判断是否出现内存泄漏和作为调优依据,下面就以内存泄漏为例简单介绍一下判断的过程:

  1. 运行jstat命令连续获取多行性能数据;
  2. 获取这几行数据中 OU 列(即已占用的老年代内存)的最小值;
  3. 间隔一段时间,重复上面两步操作,并记录下多组OU的最小值;
  4. 判断这些OU最小值的趋势,如果呈现上涨趋势,则证明该程序的老年代内存使用量在不断上涨,有可能出现内存泄漏。

2.4、jinfo:实时查看和修改JVM配置参数

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

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

jinfo不仅可以查看运行时某一个Java虚拟机参数的实际取值, 甚至可以在运行时修改部分参数,并使之立即生效。 但是,并非所有参数都支持动态修改。参数只有被标记manageable的flag可以被实时修改,即使这样这个修改能力也是极其有限的。

基本使用语法:jinfo [option] pid

option详细参数如下:

选项 选项说明
no option(没有参数) 输出全部的参数和系统属性
-flag name 输出对应名称的参数
-flag [+-]name 开启或者关闭对应名称的参数 只有被标记为manageable的参数才可以被动态修改
-flag name=value 设定对应名称的参数,不需要重启
-flags 输出全部的参数
-sysprops 输出系统属性
-h/-help 显示帮助命令

2.4.1、jinfo -sysprops:输出系统属性

2.4.2、jinfo -flags:输出全部参数

2.4.3、jinfo -flag name:输出对应名称的参数

2.4.4、jinfo -flag [+-]name:关闭和开启名称对应的属性

2.4.5、jinfo -flag name=value:设定对应名称的参数

需要注意的是:不是所有参数都可以这样修改的。

那么哪些参数可以支持动态修改呢?只有被标记manageable的flag参数可以被实时修改,具体都有哪些参数,我们可以通过 java -XX:+PrintFlagsInitial|grep manageable 去查看。

2.4.6、java -XX:+PrintFlagsInitial:查看所有JVM参数启动的初始值

2.4.7、java -XX: +PrintFlagsFinal:查看所有JVM参数的最终值

2.4.8、java -XX:+PrintCommandLineFlags 查看哪些已经被用户或者JVM设置过的详细的XX参数的名称和值

2.5、jmap

jmap(Java Virtual Machine Memory Map)是JDK提供的一个可以生成Java虚拟机的堆转储快照dump文件的命令行工具。除此以外,jmap命令还可以查看finalize执行队列、Java堆和方法区的详细信息,比如空间使用率、当前使用的什么垃圾回收器、分代情况等等。

和jinfo命令一样,在Windows系统上使用还是有一些限制的。在没有dbgeng.dll的Windows系统中,必须安装用于Windows的调试工具才能使jinfo命令正常工作,PATH环境变量应该包含jvm.dll的位置。

jmap的官方文档:jmap官方文档

基本使用语法:

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

参数详情

选项 作用
-dump 生成dump文件(Java堆转储快照),-dump:live只保存堆中的存活对象
-heap 输出整个堆空间的详细信息,包括GC的使用、堆配置信息,以及内存的使用信息等
-histo 输出堆空间中对象的统计信息,包括类、实例数量和合计容量,-histo:live只统计堆中的存活对象
-clstats 显示Java堆中元空间的类加载器的统计信息
-J <flag> 传递参数给jmap启动的jvm
-finalizerinfo 显示在F-Queue中等待Finalizer线程执行finalize方法的对象,仅linux/solaris平台有效
-permstat 以ClassLoader为统计口径输出永久代的内存状态信息,仅linux/solaris平台有效
-F 当虚拟机进程对-dump选项没有任何响应时,强制执行生成dump文件,仅linux/solaris平台有效
-h/-help 显示jinfo命令的帮助信息

注意:

  1. 如果后面没有跟任何参数,jinfo命令会显示Java虚拟机进程的内存映像信息;
  2. jmap命令会收到JDK版本的影响。

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

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

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

2.5.1、jmap:显示虚拟机内存映像信息

2.5.2、jmap -heap:输出整个堆空间的详细信息

显示Java堆的如下信息:

  • 被指定的垃圾回收算法的信息,包括垃圾回收算法的名称和垃圾回收算法的详细信息。
  • 堆的配置信息,可能是由命令行选项指定,或者由Java虚拟机根据服务器配置选择的。
  • 堆的内存空间使用信息,包括分代情况,每个代的总容量、已使用内存、可使用内存。如果某一代被继续细分(例如,年轻代),则包含细分的空间的内存使用信息。

2.5.3、jmap -histo:输出堆空间中对象的统计信息

注意:以上结果页的属性解释如下:

选项 作用
num 列出序列号
instances 该对象的实例数
bytes 该对象的总大小
class name 该对象的全限定名

我们可以通过对象实例的总大小除以对象的实例数去计算一个对象的大小。

如果我们指定了live参数,则只计算活动的对象

2.5.4、jmap -clstats:显示Java堆中元空间的类加载器的统计信息

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

这个不好演示的,大家知道这个命令就可以了,不常用。

2.5.6、jmap -dump:生成Java虚拟机的堆转储快照dump文件

具体命令为:jmap -dump:[live,]format=b,file=

参数说明:

  1. live参数是可选的,如果指定,则只转储堆中的活动对象;如果没有指定,则转储堆中的所有对象。
  2. format=b表示以hprof二进制格式转储Java堆的内存。
  3. file=<filename>用于指定快照dump文件的文件名。

2.5.7、jmap -F:强制模式

jmap -F:强制模式,如果指定的pid没有响应,可以配合-dump或-histo一起使用。此模式下,不支持live参数。

2.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 <option> <dumpfile>

参数详情

option参数 作用
-stack false|true 关闭|打开对象分配调用栈跟踪如果分配位置信息在堆转储中不可用. 则必须将此标志设置为 false. 默认值为 true
-refs false|true 关闭|打开对象引用跟踪,默认值为 true。默认情况下, 返回的指针是指向其他特定对象的对象,如反向链接或输入引用(referrers or incoming references), 会统计/计算堆中的所有对象
-port port-number 设置jhat HTTP Server的端口号,默认7000
-exclude exclude-file 执行对象查询时需要排除的数据成员。例如, 如果文件列列出了 java.lang.String.value , 那么当从某个特定对象 Object o 计算可达的对象列表时, 引用路径涉及 java.lang.String.value 的都会被排除
-baseline exclude-file 指定一个基准堆转储。在两个heap dumps中有相同 object ID 的对象会被标记为不是新的(marked as not being new),其他对象被标记为新的(new). 在比较两个不同的堆转储时很有用
-debug int 设置debug级别,0 表示不输出调试信息。 值越大则表示输出更详细的 debug 信息。
-version 启动后显示版本信息就退出
-J <flag> 传入启动参数,比如-J-Xmx512m则指定运行jhat的Java虚拟机使用的最大堆内存为512MB. 如果需要使用多个JVM启动参数,则传入多个 -Jxxxxxx

结果如下:

2.6.1、jhat扩展信息

jhat还提供了一种对象查询语言(Object Query Language),OQL有点类似SQL,可以用来查询。

OQL语句的执行页面: http://localhost:7000/oql/

OQL帮助信息页面为: http://localhost:7000/oqlhelp/

2.7、jstack:打印JVM中线程快照

jstack(Java Virtual Machine Stack Trace):是JDK提供的一个可以生成Java虚拟机当前时刻的线程快照信息的命令行工具。线程快照一般被称为threaddump或者javacore文件,是当前Java虚拟机中每个线程正在执行的Java线程、虚拟机内部线程和可选的本地方法堆栈帧的集合。对于每个方法栈帧,将会显示完整的类名、方法名、字节码索引(bytecode index,BCI)和行号。生成的线程快照可以用于定位线程出现长时间停顿的原因,例如:

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

jstack官方文档:jstack

jstack使用说明:jstack [options] pid

option参数 作用
-F 当正常输出的请求不被响应时,强制输出线程堆栈
-l 除堆栈外,还会显示关于锁的附加信息
-m 如果调用本地方法的话,可以显示C/C++的堆栈

注意:如果不加option参数,jstack命令会显示Java虚拟机当前时刻的线程快照信息。,如下图所示:

2.7.1、jstack -F:强制显示快照信息

2.7.2、jstack -l:显示方法栈帧以及锁的附加信息

2.7.3、jstack -m:显示混合栈帧及本地方法栈信息

2.8、jcmd:多功能命令行工具

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

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

jcmd官方文档:jcmd

jcmd使用说明:jcmd pid [options]

options参数如下所示:

options参数 作用
-l 列出所有的JVM进程,等价于jps命令
Thread.print 可以替换jstack指令,打印线程快照
GC.class_histogram 可以替换jmap中的-histo操作,查看类实例柱状图统计信息
GC.heap_dump 可以替换jmap中的-dump操作,dump堆转储文件
GC.run 可以查看GC的执行情况
VM.uptime 可以查看程序的总执行时间,可以替换jstat指令中的-t操作
VM.system_properties 可以替换jinfo-sysprops进程id
VM.flags 可以获取JVM的配置参数信息
help 针对指定的进程,列出支持的所有具体命令

2.9、jstatd:远程主机信息收集

jstatd是一个基于RMI(Remove Method Invocation)的服务程序,它用于监控基于HotSpot的JVM中资源的创建及销毁,并且提供了一个远程接口允许远程的监控工具连接到本地的JVM执行命令。

jstatd是基于RMI的,所以在运行jstatd的服务器上必须存在RMI注册中心,如果没有通过选项"-p port"指定要连接的端口,jstatd会尝试连接RMI注册中心的默认端口。

jstatd使用说明:jstatd [-nr] [-p port] [-n rminame]

options参数 作用
-nr 如果RMI注册中心没有找到,不会创建一个内部的RMI注册中心。
-p RMI注册中心的端口号,默认为1099。
-n rminame 默认为JStatRemoteHost;如果同一台主机上同时运行了多个jstatd服务,rminame可以用于唯一确定一个jstatd服务;这里需要注意一下,如果开启了这个选项,那么监控客户端远程连接时,必须同时指定hostid及vmid,才可以唯一确定要连接的服务,这个可以参看jps章节中列出远程服务器上Java进程的示例。
-J 用于传递jvm选项到由javac调用的java加载器中,例如,“-J-Xms48m”将把启动内存设置为48M,使用-J选项可以非常方便的向基于Java的开发的底层虚拟机应用程序传递参数。

jstatd服务只能监视具有适当的本地访问权限的JVM,因此jstatd进程与被监控的JVM必须运行在相同的用户权限中。但是有一些特殊的用户权限,如基于UNIX(TM)为系统的root用户,它有权限访问系统中所有JVM的资源,如果jstatd进程运行在这种权限中,那么它可以监视系统中的所有JVM,但是这也带来了额外的安全问题。

jstatd服务不会对客户端进行任何的验证,因此运行了jstatd服务的JVMs,网络上的任何用户的都具有访问权限,这种暴露不是我们所希望的,因此在启动jstatd之前本地安全策略必须要加以考虑,特别是在生产环境中或者是在不安全的网络环境中。

三、JVM内存监控及诊断工具

使用上一章命令行工具或组合能帮您获取目标Java应用性能相关的基础信息,但它们存在下列局限:

  1. 无法获取方法级别的分析数据,如方法间的调用关系、各方法的调用次数和调用时间等(这对定位应用性能瓶颈至关重要);
  2. 要求用户登录到目标 Java 应用所在的宿主机上,使用起来不是很方便;
  3. 分析数据通过终端输出,结果展示不够直观。

为此,JDK提供了一些内存泄漏的分析工具,如jconsole,jvisualvm等,用于辅助开发人员定位问题。

JDK自带的工具:

  1. jconsole:JDK自带的可视化监控工具。查看Java应用程序的运行概况、监控堆信息、永久区(或元空间)使用情况、类加载情况等;
  2. Visual VM:Visual VM是一个工具,它提供了一个可视界面,用于查看Java虚拟机上运行的基于Java技术的应用程序的详细信息;
  3. JMC:Java Mission Control,内置Java Flight Recorder。能够以极低的性能开销收集Java虚拟机的性能数据。

第三方工具:

  1. MAT:MAT(Memory Analyzer Tool)是基于Eclipse的内存分析工具,是一个快速、功能丰富的Java heap分析工具,它可以帮助我们查找内存泄漏和减少内存消耗
  2. JProfiler:商业软件,需要付费。功能强大。

3.1、jconsole

jconsole:从Java5开始,在JDK中自带的java监控和管理控制台。用于对JVM中内存、线程和类等的监控,是一个基于JMX(java management extensions)的GUI性能监控工具。

官方地址:Using JConsole - Java SE Monitoring and ManagementGuide

jconsole的位置在bin目录下,找到jconsole.exe,双击即可打开

打开后页面是这样的的,我们可以选择本地进程,也可以选择远程的,本地的可以直接选择你要监控的进程即可,远程的可以用ip:port

看到下面这个页面就说明启动成功了

内存选项下面可以查看具体的JVM内存使用情况

可以在VM概要中查看一些基本信息包括JVM参数等等,其他功能大家可以自己探索开发。

3.2、Visual VM

Visual VM是一个功能强大的多合一故障诊断和性能监控的可视化工具。它集成了多个JDK命令行工具,使用Visual VM可用于显示虚拟机进程及进程的配置和环境信息(jps,jinfo),监视应用程序的CPU、GC、堆、方法区及线程的信息(jstat、jstack)等,甚至代替JConsole。在JDK 6 Update 7以后,Visual VM便作为JDK的一部分发布(VisualVM 在JDK/bin目录下)即:它完全免费。

主要功能:

  1. 生成/读取堆内存/线程快照
  2. 查看JVM参数和系统属性
  3. 查看运行中的虚拟机进程
  4. 程序资源的实时监控
  5. JMX代理连接、远程环境监控、CPU分析和内存分析

官方地址:VisualVM: Home

双击打开,出现下面页面即可

选择我们要监控的项目即可

可以选择监控的内容、线程等等

3.2.1、Visual VM分析dump

Visual VM不仅能系统的展示应用程序的相关信息,还可以分析我们生成的dump文件。

首先生成hprof文件

打开Visual VM,选择文件->装入 选择对应的文件即可。

3.3、Eclipse MAT

MAT(Memory Analyzer Tool)工具是一款功能强大的Java堆内存分析器。可以用于查找内存泄漏以及查看内存消耗情况。MAT是基于Eclipse开发的,不仅可以单独使用,还可以作为插件的形式嵌入在Eclipse中使用。是一款免费的性能分析工具,使用起来非常方便。

官方地址: Eclipse Memory Analyzer Open Source Project | The Eclipse Foundation

MAT可以分析heap dump文件。在进行内存分析时,只要获得了反映当前设备内存映像的hprof文件,通过MAT打开就可以直观地看到当前的内存信息。一般说来,这些内存信息包含:

  1. 所有的对象信息,包括对象实例、成员变量、存储于栈中的基本类型值和存储于堆中的其他对象的引用值。
  2. 所有的类信息,包括classloader、类名称、父类、静态变量等
  3. GCRoot到所有的这些对象的引用路径
  4. 线程信息,包括线程的调用栈及此线程的线程局部变量(TLS)

MAT 不是一个万能工具,它并不能处理所有类型的堆存储文件。但是比较主流的厂家和格式,例如Sun,HP,SAP 所采用的 HPROF 二进制堆存储文件,以及 IBM的 PHD 堆存储文件等都能被很好的解析。

最吸引人的还是能够快速为开发人员生成内存泄漏报表,方便定位问题和分析问题。虽然MAT有如此强大的功能,但是内存分析也没有简单到一键完成的程度,很多内存问题还是需要我们从MAT展现给我们的信息当中通过经验和直觉来判断才能发现。

3.4、JProfiler

在运行Java的时候有时候想测试运行时占用内存情况,这时候就需要使用测试工具查看了。在eclipse里面有 Eclipse Memory Analyzer tool(MAT)插件可以测试,而在IDEA中也有这么一个插件,就是JProfiler。JProfiler 是由 ej-technologies 公司开发的一款 Java 应用性能诊断工具。功能强大,但是收费。

特点:

  1. 使用方便、界面操作友好(简单且强大)
  2. 对被分析的应用影响小(提供模板)
  3. CPU,Thread,Memory分析功能尤其强大
  4. 支持对jdbc,noSql,jsp,servlet,socket等进行分析
  5. 支持多种模式(离线,在线)的分析
  6. 支持监控本地、远程的JVM
  7. 跨平台,拥有多种操作系统的安装版本

主要功能:

  1. 方法调用:对方法调用的分析可以帮助您了解应用程序正在做什么,并找到提高其性能的方法;
  2. 内存分配:通过分析堆上对象、引用链和垃圾收集能帮您修复内存泄露问题,优化内存使用;
  3. 线程和锁:JProfiler提供多种针对线程和锁的分析视图助您发现多线程问题;
  4. 高级子系统:许多性能问题都发生在更高的语义级别上。例如,对于JDBC调用,您可能希望找出执行最慢的SQL语句。JProfiler支持对这些子系统进行集成分析。

官网地址:Java Profiler - JProfiler

数据采集方式:

JProfier数据采集方式分为两种:Sampling(样本采集)Instrumentation(重构模式)

Instrumentation:这是JProfiler全功能模式。在class加载之前,JProfier把相关功能代码写入到需要分析的class的bytecode中,对正在运行的jvm有一定影响。

  • 优点:功能强大。在此设置中,调用堆栈信息是准确的;
  • 缺点:若要分析的class较多,则对应用的性能影响较大,CPU开销可能很高(取决于Filter的控制)。因此使用此模式一般配合Filter使用,只对特定的类或包进行分析。

Sampling:类似于样本统计,每隔一定时间(5ms)将每个线程栈中方法栈中的信息统计出来。

  • 优点:对CPU的开销非常低,对应用影响小(即使你不配置任何Filter);
  • 缺点:一些数据/特性不能提供(例如:方法的调用次数、执行时间)。

注:JProfiler本身没有指出数据的采集类型,这里的采集类型是针对方法调用的采集类型。因为JProfiler的绝大多数核心功能都依赖方法调用采集的数据,所以可以直接认为是JProfiler的数据采集类型。

Instrumentation模式:

内存视图:

Live memory 内存剖析:class/class instance的相关信息。例如对象的个数,大小,对象创建的方法执行栈,对象创建的热点。

  1. 所有对象 All Objects:显示所有加载的类的列表和在堆上分配的实例数。只有Java 1.5(JVMTI)才会显示此视图。
  2. 记录对象 Record Objects:查看特定时间段对象的分配,并记录分配的调用堆栈。
  3. 分配访问树 Allocation Call Tree:显示一棵请求树或者方法、类、包或对已选择类有带注释的分配信息的J2EE组件。
  4. 分配热点 Allocation Hot Spots:显示一个列表,包括方法、类、包或分配已选类的J2EE组件。可以标注当前值并且显示差异值。对于每个热点都可以显示跟踪记录树。
  5. 类追踪器 Class Tracker:类跟踪视图可以包含任意数量的图表,显示选定的类和包的实例与时间。

堆遍历:

cpu视图:

JProfiler 提供不同的方法来记录访问树以优化性能和细节。线程或者线程组以及线程状况可以被所有的视图选择。所有的视图都可以聚集到方法、类、包或J2EE组件等不同层上。

  1. 访问树 Call Tree:显示一个积累的自顶向下的树,树中包含所有在JVM中已记录的访问队列。请求树可以根据Servlet和JSP对URL的不同需要进行拆分。
  2. 热点 Hot Spots:显示消耗时间最多的方法的列表。对每个热点都能够显示回溯树。该热点可以按照方法请求,JDBC,JMS和JNDI服务请来进行计算。
  3. 访问图 Call Graph:显示一个从已选方法、类、包或J2EE组件开始的访问队列的图。
  4. 方法统计 Method Statistis:显示一段时间内记录的方法的调用时间细节。

线程视图:

JProfiler通过对线程历史的监控判断其运行状态,并监控是否有线程阻塞产生,还能将一个线程所管理的方法以树状形式呈现。对线程剖析。

  1. 线程历史 Thread History:显示一个与线程活动和线程状态在一起的活动时间表。
  2. 线程监控 Thread Monitor:显示一个列表,包括所有的活动线程以及它们目前的活动状况。
  3. 线程转储 Thread Dumps:显示所有线程的堆栈跟踪。

线程分析主要关心三个方面:

  1. web容器的线程最大数。比如:Tomcat的线程容量应该略大于最大并发数。
  2. 线程阻塞
  3. 线程死锁

监控和锁:

所有线程持有锁的情况以及锁的信息。观察JVM的内部线程并查看状态:

  1. 死锁探测图表 Current Locking Graph:显示JVM中的当前死锁图表。
  2. 目前使用的监测器 Current Monitors:显示目前使用的监测器并且包括它们的关联线程。
  3. 锁定历史图表 Locking History Graph:显示记录在JVM中的锁定历史。
  4. 历史检测记录 Monitor History:显示重大的等待事件和阻塞事件的历史记录。
  5. 监控器使用统计 Monitor Usage Statistics:显示分组监测,线程和监测类的统计监测数据

3.5、Arthas

上述工具都必须在服务端项目进程中配置相关的监控参数,然后工具通过远程连接到项目进程,获取相关的数据。这样就会带来一些不便,比如线上环境的网络是隔离的,本地的监控工具根本连不上线上环境。并且类似于Jprofiler这样的商业工具,是需要付费的。

那么有没有一款工具不需要远程连接,也不需要配置监控参数,同时也提供了丰富的性能监控数据呢?

阿里巴巴开源的性能分析神器Arthas应运而生。

Arthas是Alibaba开源的Java诊断工具,在线排查问题,无需重启;动态跟踪Java代码;实时监控JVM状态。Arthas 支持JDK 6+,支持Linux/Mac/Windows,采用命令行交互模式,同时提供丰富的 Tab 自动补全功能,进一步方便进行问题的定位和诊断。当你遇到以下类似问题而束手无策时,Arthas可以帮助你解决:

  • 这个类从哪个 jar 包加载的?为什么会报各种类相关的 Exception?
  • 我改的代码为什么没有执行到?难道是我没 commit?分支搞错了?
  • 遇到问题无法在线上 debug,难道只能通过加日志再重新发布吗?
  • 线上遇到某个用户的数据处理有问题,但线上同样无法 debug,线下无法重现!
  • 是否有一个全局视角来查看系统的运行状况?
  • 有什么办法可以监控到JVM的实时运行状态?
  • 怎么快速定位应用的热点,生成火焰图?

官方地址:快速入门 — Arthas 3.6.2 文档

安装方式:如果速度较慢,可以尝试国内的码云Gitee下载。

wget https://io/arthas/arthas-boot.jar

wget https://arthas/gitee/io/arthas-boot.jar

Arthas只是一个java程序,所以可以直接用java -jar运行。

除了在命令行查看外,Arthas目前还支持 Web Console。在成功启动连接进程之后就已经自动启动,可以直接访问 http://127.0.0.1:8563/ 访问,页面上的操作模式和控制台完全一样。

3.5.1、基本命令

quit/exit 退出当前 Arthas客户端,其他 Arthas喜户端不受影响stop/shutdown 关闭 Arthas服务端,所有 Arthas客户端全部退出help 查看命令帮助信息cat 打印文件内容,和linux里的cat命令类似echo 打印参数,和linux里的echo命令类似grep 匹配查找,和linux里的gep命令类似tee 复制标隹输入到标准输出和指定的文件,和linux里的tee命令类似pwd 返回当前的工作目录,和linux命令类似cls 清空当前屏幕区域session 查看当前会话的信息reset 重置增强类,将被 Arthas增强过的类全部还原, Arthas服务端关闭时会重置所有增强过的类version 输出当前目标Java进程所加载的 Arthas版本号history 打印命令历史keymap Arthas快捷键列表及自定义快捷键

3.5.2、JVM相关

dashboard 当前系统的实时数据面板thread 查看当前JVM的线程堆栈信息jvm 查看当前JVM的信息sysprop 查看和修改JVM的系统属性sysem 查看JVM的环境变量vmoption 查看和修改JVM里诊断相关的optionperfcounter 查看当前JVM的 Perf Counter信息logger 查看和修改loggergetstatic 查看类的静态属性ognl 执行ognl表达式mbean 查看 Mbean的信息heapdump dump java heap,类似jmap命令的 heap dump功能

3.5.3、class/classloader相关

sc 查看JVM已加载的类信息-d 输出当前类的详细信息,包括这个类所加载的原始文件来源、类的声明、加载的Classloader等详细信息。如果一个类被多个Classloader所加载,则会出现多次-E 开启正则表达式匹配,默认为通配符匹配-f 输出当前类的成员变量信息(需要配合参数-d一起使用)-X 指定输出静态变量时属性的遍历深度,默认为0,即直接使用toString输出sm 查看已加载类的方法信息-d 展示每个方法的详细信息-E 开启正则表达式匹配,默认为通配符匹配jad 反编译指定已加载类的源码mc 内存编译器,内存编译.java文件为.class文件retransform 加载外部的.class文件, retransform到JVM里redefine 加载外部的.class文件,redefine到JVM里dump dump已加载类的byte code到特定目录classloader 查看classloader的继承树,urts,类加载信息,使用classloader去getResource-t 查看classloader的继承树-l 按类加载实例查看统计信息-c 用classloader对应的hashcode来查看对应的 Jar urls

3.5.4、monitor/watch/trace相关

monitor 方法执行监控,调用次数、执行时间、失败率-c 统计周期,默认值为120秒watch 方法执行观测,能观察到的范围为:返回值、抛出异常、入参,通过编写groovy表达式进行对应变量的查看-b 在方法调用之前观察(默认关闭)-e 在方法异常之后观察(默认关闭)-s 在方法返回之后观察(默认关闭)-f 在方法结束之后(正常返回和异常返回)观察(默认开启)-x 指定输岀结果的属性遍历深度,默认为0trace 方法内部调用路径,并输出方法路径上的每个节点上耗时-n 执行次数限制stack 输出当前方法被调用的调用路径tt 方法执行数据的时空隧道,记录下指定方法每次调用的入参和返回信息,并能对这些不同的时间下调用进行观测

3.3.5、其他

jobs 列出所有jobkill 强制终止任务fg 将暂停的任务拉到前台执行bg 将暂停的任务放到后台执行grep 搜索满足条件的结果plaintext 将命令的结果去除ANSI颜色wc 按行统计输出结果options 查看或设置Arthas全局开关profiler 使用async-profiler对应用采样,生成火焰图

四、JVM内存泄漏实战分析

4.1、内存泄漏代码准备

package com.sxx;import java.util.ArrayList;
import java.util.List;
import java.util.UUID;public class TestJvmOutOfMemory {public static void main(String[] args) {List<Object> list = new ArrayList<>();for (int i = 0; i < 10000000; i++) {String str = "";for (int j = 0; j < 1000; j++) {str += UUID.randomUUID().toString();}list.add(str);}System.out.println("ok");}
}

为了让这段代码发生内存泄漏,还需要限制JVM内存大小,因此需要添加上以下参数:

-Xms8m -Xmx8m -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=D:\dump.hprof

即限制JVM内存大小为8M,如果出现内存泄漏,就会生成dump文件,存放文件的位置在D:\dump.hprof。

执行代码结果如下:

4.2、分析dump文件

第一步:查找占用内存特别大的对象,我们可以按照对象占用内存大小进行排列,筛选出占用内存最大的20个对象;

第二部:根据占用内存比较大的对象去排查有异常的对象,可以发现ArrayList和Object占用的对象很大,可以确定是这个导致内存泄漏;

第三步:结合代码和Visual VM显示的#9可以定位到是我们main中的第9行发生意外。

4.3、内存溢出和内存泄漏的区别

有些人傻傻分不清内存泄漏和内存溢出的区别,这里简单做个科普:

  1. 内存溢出是指程序在申请内存时,没有足够的内存空间供其使用,会出现Out Of Memory(就是我们常说的OOM)。
  2. 内存泄漏是指程序在申请内存后,无法释放以申请的内存空间。一次内存泄漏的危害是可以忽略不计的,但是如果一直内存泄漏造成内存泄漏的堆积后果很严重,因为分配的内存空间得不到释放,不管你有多少内存迟早都会被消耗光。

五、JVM死锁实战分析

5.1、死锁代码准备

package com.sxx;public class TestDeadLock {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 {@Overridepublic 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 {@Overridepublic 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 的锁!");}}}}
}

结果如下:

可以看到:结果页面一直卡住不动,我们可以尝试分析一下。

5.2、死锁分析

我们先使用jps看一下:

使用jstack查看下线程间状态:

从上图可以看出:出现死锁的两个线程分别位于TestDeadLock.java:42和TestDeadLock.java:24,我们可以根据定位的结果修正自己的代码。

JVM内存监控及调优分析相关推荐

  1. jinfo java_Java自带的JVM性能监控及调优工具(jps、jinfo、jstat、jmap、javap)使用介...

    JVM介绍 JVM是Java Virtual Machine(Java虚拟机)的缩写,JVM是一种用于计算设备的规范,它是一个虚构出来的计算机,是通过在实际的计算机上仿真模拟各种计算机功能来实现的. ...

  2. 调优 | 别再说你不会 JVM 性能监控和调优了

    Hi ! 我是小小,今天是本周的最后一篇,本篇将会着重讲解关于 JVM 调优和性能监控方面的内容 常用工具 常用工具主要有 JDK 自带工具与 Arthas 这两种工具. JDK 自带工具 jps 虚 ...

  3. visualvm远程监控jvm_别再说你不会 JVM 性能监控和调优了,看完这篇再发言

    常用工具 常用工具主要有 JDK 自带工具与 Arthas 这两种工具. JDK 自带工具 jps 虚拟机进程状况工具 用于查看虚拟机进程状况的工具 命令示例 $ jps15236 Jps14966 ...

  4. JVM性能监控及调优篇

    一,概述 1,背景说明 1)生产环境中的问题 生产环境发生了内存溢出该如何处理 生产环境应该给服务器分配多少内存合适? 如何对垃圾回收器的性能进行调优? 生产环境CPU负载飙高该如何处理? 生产环境应 ...

  5. JVM性能监控与调优之概述命令行篇

    背景说明 生产环境中出现的问题 生产环境发生了内存溢出该如何处理? 生产环境应该给服务器分配多少内存合适? 如何对垃圾回收器的性能进行调优? 生产环境 CPU 负载期飙高该如何处理? 生产环境应该给应 ...

  6. Linux系统性能监控与调优

    基本概念 QPS:query per second, 1秒内完成的请求数 RT:response time, 1个请求完成的时间 Throughput越大,Latency会越差.因为请求量过大,系统太 ...

  7. Tomcat性能调优-JVM监控与调优

    参数设置 在Java虚拟机的参数中,有3种表示方法用"ps -ef |grep "java"命令,可以得到当前Java进程的所有启动参数和配置参数: 标准参数(-),所有 ...

  8. Java系列笔记(4) - JVM监控与调优【转】

    Java系列笔记(4) - JVM监控与调优[转] 目录 参数设置 收集器搭配 启动内存分配 监控工具和方法 调优方法 调优实例       光说不练假把式,学习Java GC机制的目的是为了实用,也 ...

  9. [java] JVM监控与调优

    原文出处:http://www.cnblogs.com/zhguang/p/java-jvm-gc.html   光说不练假把式,学习Java GC机制的目的是为了实用,也就是为了在JVM出现问题时分 ...

最新文章

  1. css 实现app图标样式_界面按钮样式丑?不可能!16款css实现炫酷按钮
  2. 【IT资讯】年薪170万码农征友,条件让网友炸锅……
  3. Centos 7 KVM安装win10
  4. [html] 如何禁止input输入的历史记录
  5. hashmap另一种初始化
  6. 3D卷积GAN飞起!微软“可缩放”新框架只需2D数据即可生成逼真3D模型
  7. 史上最奢华AirPods登场 售价直接翻四倍却还算良心
  8. [SCM]源码管理 - SVN的备份和还原
  9. idea 新建的java项目没发run_IntelliJ IDEA 如何创建一个普通的java项目,及创建java文件并运行...
  10. 数学建模之回归分析加例题详解(MATLAB实现)
  11. Word中英语音标出现乱码情况,解决办法
  12. 使用html5与js实现音乐播放器
  13. 微信errcode大全
  14. https是什么_http与https的区别
  15. 解析新浪微博JSON
  16. 《实战 Java 高并发程序设计》笔记——第3章 JDK 并发包(二)
  17. 手机便签内容如何保存到电脑
  18. 我创业之路的“足迹”连缀——2008年新浪首届“我的创业路”征文
  19. 2015年英语学习——8月
  20. 电气设计参考图纸,有高低压电气设计图纸、自控设计图纸和污水处理工程电气自控图

热门文章

  1. 最近一些的有意思的思考-记录成长
  2. 第七章 得数据者得天下,商业竞争中的大数据
  3. 3.【Linux】ubuntu18.04安装搜狗输入法
  4. 嵌入式计算机答辩标准,陈月昆答辩公告
  5. 全球及中国同步电容器行业研究及十四五规划分析报告(2022)
  6. c语言迷宫游戏课程设计,迷宫游戏c语言程序课程设计.docx
  7. ES聚合之Metric聚合语法讲解
  8. iOS7 下载酷狗 微信 抖音 网易云音乐app ipk的下载办法App Store不能下载应用程序
  9. 网络游戏外挂制作 (转贴)
  10. git 查看当前仓库地址以及设置新的仓库地址