"🙊🙊🙊This tutorial is experimental and unsupported."
上期课后作业经典回答
- 问题: Java 常见的垃圾收集器有哪些?
实际上,垃圾收集器(GC,Garbage Collector)是和具体 JVM 实现紧密相关的,不同厂商(IBM、Oracle),不同版本的 JVM,提供的选择也不同。接下来,我来谈谈最主流的 Oracle JDK。
- Serial GC,它是最古老的垃圾收集器,“Serial”体现在其收集工作是单线程的,并且在进行垃圾收集过程中,会进入臭名昭著的“Stop-The-World”状态。当然,其单线程设计也意味着精简的 GC 实现,无需维护复杂的数据结构,初始化也简单,所以一直是 Client 模式下 JVM 的默认选项。从年代的角度,通常将其老年代实现单独称作 Serial Old,它采用了标记 - 整理(Mark-Compact)算法,区别于新生代的复制算法。
-XX:UseSerialGC
- ParNew GC,很明显是个新生代 GC 实现,它实际是 Serial GC 的多线程版本,最常见的应用场景是配合老年代的 CMS GC 工作,下面是对应参数
-XX:+UseConcMarkSweepGC -XX:+UseParNewGC
CMS(Concurrent Mark Sweep) GC,基于标记 - 清除(Mark-Sweep)算法,设计目标是尽量减少停顿时间,这一点对于 Web 等反应时间敏感的应用非常重要,一直到今天,仍然有很多系统使用 CMS GC。但是,CMS 采用的标记 - 清除算法,存在着内存碎片化问题,所以难以避免在长时间运行等情况下发生 full GC,导致恶劣的停顿。另外,既然强调了并发(Concurrent),CMS 会占用更多 CPU 资源,并和用户线程争抢。
Parrallel GC,在早期 JDK 8 等版本中,它是 server 模式 JVM 的默认 GC 选择,也被称作是吞吐量优先的 GC。它的算法和 Serial GC 比较相似,尽管实现要复杂的多,其特点是新生代和老年代 GC 都是并行进行的,在常见的服务器环境中更加高效。
-XX:+UseParallelGC
// Parallel GC 引入了开发者友好的配置项,我们可以直接设置暂停时间或吞吐量等目标
-XX:MaxGCPauseMillis=value
-XX:GCTimeRatio=N // GC 时间和用户时间比例 = 1 / (N+1)
- G1 GC 这是一种兼顾吞吐量和停顿时间的 GC 实现,是 Oracle JDK 9 以后的默认 GC 选项。G1 可以直观的设定停顿时间的目标,相比于 CMS GC,G1 未必能做到 CMS 在最好情况下的延时停顿,但是最差情况要好很多。G1 GC 仍然存在着年代的概念,但是其内存结构并不是简单的条带式划分,而是类似棋盘的一个个 region。Region 之间是复制算法,但整体上实际可看作是标记 - 整理(Mark-Compact)算法,可以有效地避免内存碎片,尤其是当 Java 堆非常大的时候,G1 的优势更加明显。
衍生的面试问题:
- 垃圾收集的算法有哪些?如何判断一个对象是否可以回收?
- 垃圾收集器工作的基本流程。
大纲 - 思维导图
概述
前面两篇文章对于《深入理解Java虚拟机》的虚拟机内存分配与回收技术两章,分别做了总结记录,已经建立了一套比较完整的理论基础。接下来我们从实践的角度去了解虚拟机内存管理,定位问题的时候,知识、经验是关键基础,数据是依据,工具是运用知识处理数据的手段。
数据主要包括:
- 运行日志
- 异常堆栈
- GC日志
- 线程快照(thread dump / javacore文件)
- 堆转储快照(heapdump/hprof文件)等
JDK命令行工具
Java安装目录 bin目录下有很多官方提供的命令行程序,今天我们俩了解下虚拟机性能监控与故障处理工具
名称 | 主要作用 |
---|---|
jps | JVM Process Status Tool, 显示指定系统内所有的HotSpot的虚拟机进程 |
jstat | JVM Statistics Monitoring Tool,用于收集HotSpot各方面的运行数据 |
jinfo | Configuration Info for Java,显示虚拟机的配置信息 |
jmap | Memory Map for Java,生成Java内存转储快照(dump文件) |
jhat | JVM Heap Dump Browser,用于分析dump文件,会创建HTTP/HTML服务器,可以在浏览器上浏览 |
jstack | Stack Trace for Java,显示虚拟机的线程快照 |
jps: 虚拟机进程状况工具
不仅名字像lunix主机的ps命令,功能也相似,JDK中的jps命令(帮助文档)可以列举出正在运行的虚拟机进程,并显示虚拟机执行主类(Main Class,main函数所在的类)名称以及这些进程的本地虚拟机唯一ID(Local Virtual Machine Identifier ,LVMID)。
使用频率最高的JDK命令行工具,以为内其他的JDK工具大多需要输入它查询到LVMID来确定要监控的是哪一个虚拟机进程。 对于本地虚拟机进程来讲,LVMID和操作系统的进程ID(Process Identifer ,PID) 一致。
语法:
usage: jps [-help]
jps [-q] [-mlvV] [<hostid>]
Definitions:
<hostid>: <hostname>[:<port>]
在默认情况下,jps的输出信息包括 Java 进程的进程 ID 以及主类名。我们还可以通过追加参数,来打印额外的信息。例如,-l将打印模块名以及包名;-v将打印传递给 Java 虚拟机的参数(如-XX:+UnlockExperimentalVMOptions -XX:+UseZGC);-m将打印传递给主类的参数。
需要注意的是,如果某 Java 进程关闭了默认开启的UsePerfData参数(即使用参数-XX:-UsePerfData),那么jps命令(以及下面介绍的jstat)将无法探知该 Java 进程。
当获得 Java 进程的进程 ID 之后,我们便可以调用接下来介绍的各项监控及诊断工具了。
jstat: 虚拟机统计工具监视工具
jstat (JVM Statistics Monitoring Tool)(帮助文档)用于监视虚拟机各种运行状态信息的命令行工具。 它可以显示本地或者远程虚拟机进程中的类装载、内存、垃圾会搜、JIT编译等运行数据。
在没有图形化界面的服务器上,它将是运行期定位虚拟机性能问题的首选工具。
jstat 命令格式为:
Usage: jstat -help|-options
jstat -<option> [-t] [-h<lines>] <vmid> [<interval> [<count>]]
Definitions:
<option> An option reported by the -options option
<vmid> Virtual Machine Identifier. A vmid takes the following form:
<lvmid>[@<hostname>[:<port>]]
Where <lvmid> is the local vm identifier for the target
Java virtual machine, typically a process id; <hostname> is
the name of the host running the target Java virtual machine;
and <port> is the port number for the rmiregistry on the
target host. See the jvmstat documentation for a more complete
description of the Virtual Machine Identifier.
<lines> Number of samples between header lines.
<interval> Sampling interval. The following forms are allowed:
<n>["ms"|"s"]
Where <n> is an integer and the suffix specifies the units as
milliseconds("ms") or seconds("s"). The default units are "ms".
<count> Number of samples to take before terminating.
-J<flag> Pass <flag> directly to the runtime system.
几点说明:
- 对于命令格式中的VMID和LVMID,如果是本地虚拟机进程,两者一致。如果是远程虚拟机进程,则VMID的格式为
[protocol:][//]lvmind[@hostname[:port]/servername]
- interval : 查询间隔
- count:查询次数 如果省略interval和count,则只查询一次
假设需要250毫秒查询一次进程3245的垃圾收集状况,一共查询20次
jstat -gc 3245 250 20
jstat的主要选项:
举例说明:
[dashu@*** ~]$ sudo jstat -gcutil 26096 1000 10
S0 S1 E O M CCS YGC YGCT FGC FGCT GCT
0.00 48.00 17.05 7.22 94.77 91.65 515 8.555 3 0.652 9.207
0.00 48.00 17.69 7.22 94.77 91.65 515 8.555 3 0.652 9.207
0.00 48.00 18.84 7.22 94.77 91.65 515 8.555 3 0.652 9.207
0.00 48.00 19.99 7.22 94.77 91.65 515 8.555 3 0.652 9.207
0.00 48.00 20.59 7.22 94.77 91.65 515 8.555 3 0.652 9.207
0.00 48.00 21.20 7.22 94.77 91.65 515 8.555 3 0.652 9.207
0.00 48.00 21.84 7.22 94.77 91.65 515 8.555 3 0.652 9.207
0.00 48.00 23.10 7.22 94.77 91.65 515 8.555 3 0.652 9.207
0.00 48.00 25.04 7.22 94.77 91.65 515 8.555 3 0.652 9.207
0.00 48.00 27.28 7.22 94.77 91.65 515 8.555 3 0.652 9.207
- S0: Survivor0是空的
- S1:Survivor1使用了48%的空间
- E:Eden 使用了 17.05%的空间
- O: 老年代使用了 7.22%的空间
- M: 永久代?
- CCS: ?
- YGC: Minor GC表示程序启动以来,发生了515次Minor GC
- YGCT:Young GC Time 耗时为8.555秒
- FGC:Full GC 表示程序启动以来,发生了3次Minor GC
- FGCT:Full GC Time 耗时为0.652秒
- GCT:表示GC Time ,所有的GC总耗时 8.555秒
使用jstat工具在纯文本状态下监视虚拟机状态的变化,确实不如后面将会提到的VisualVM等可视化的监视工具直接以图表展现那样直观。但许多服务器管理员都习惯了在文本控制台中工作,直接在控制台中使用jstat命令依然是一种常用的监控方式。
jinfo: Java配置信息工具
jinfo(Configuration Info for Java)(帮助文档)的作用是实时地查看和调整虚拟机各项参数。
命令语法格式为:
Usage:
jinfo [option] <pid>
(to connect to running process)
jinfo [option] <executable <core>
(to connect to a core file)
jinfo [option] [server_id@]<remote server IP or hostname>
(to connect to remote debug server)
where <option> is one of:
-flag <name> to print the value of the named VM flag
-flag [+|-]<name> to enable or disable the named VM flag
-flag <name>=<value> to set the named VM flag to the given value
-flags to print VM flags
-sysprops to print Java system properties
<no option> to print both of the above
-h | -help to print this help message
使用举例:
[root@*** ~]# jinfo -flag MaxPermSize 5653
-XX:MaxPermSize=85983232
jmap: Java内存映射工具
jmap(Memory Map for Java)(帮助文档)命令用于生成堆转储快照(一般称为heapdump或dump文件)。如果不使用jmap命令,要想获取Java堆转储快照,还有一些比较“暴力”的手段:譬如在第2章中用过的-XX:+HeapDumpOnOutOfMemoryError参数,可以让虚拟机在OOM异常出现之后自动生成dump文件,通过-XX:+HeapDumpOnCtrlBreak参数则可以使用[Ctrl]+[Break]键让虚拟机生成dump文件,又或者在Linux系统下通过Kill-3命令发送进程退出信号“吓唬”一下虚拟机,也能拿到dump文件。
命令语法格式为:
Usage:
jmap [option] <pid>
(to connect to running process)
jmap [option] <executable <core>
(to connect to a core file)
jmap [option] [server_id@]<remote server IP or hostname>
(to connect to remote debug server)
where <option> is one of:
<none> to print same info as Solaris pmap
-heap to print java heap summary
-histo[:live] to print histogram of java object heap; if the "live"
suboption is specified, only count live objects
-clstats to print class loader statistics
-finalizerinfo to print information on objects awaiting finalization
-dump:<dump-options> to dump java heap in hprof binary format
dump-options:
live dump only live objects; if not specified,
all objects in the heap are dumped.
format=b binary format
file=<file> dump heap to <file>
Example: jmap -dump:live,format=b,file=heap.bin <pid>
-F force. Use with -dump:<dump-options> <pid> or -histo
to force a heap dump or histogram when <pid> does not
respond. The "live" suboption is not supported
in this mode.
-h | -help to print this help message
-J<flag> to pass <flag> directly to the runtime system
jmap的主要选项:
使用举例,对pid=19935的进程输出dump信息:
[root@*** ~]# jmap -dump:format=b,file=test.bin 19935
Dumping heap to /root/test.bin ...
Heap dump file created
jhat: 虚拟机堆转储快照分析工具
Sun JDK提供jhat(JVM Heap Analysis Tool)(帮助文档)命令与jmap搭配使用,来分析jmap生成的堆转储快照。jhat内置了一个微型的HTTP/HTML服务器,生成dump文件的分析结果后,可以在浏览器中查看。
命令语法格式:
Usage: jhat [-stack <bool>] [-refs <bool>] [-port <port>] [-baseline <file>] [-debug <int>] [-version] [-h|-help] <file>
-J<flag> Pass <flag> directly to the runtime system. For
example, -J-mx512m to use a maximum heap size of 512MB
-stack false: Turn off tracking object allocation call stack.
-refs false: Turn off tracking of references to objects
-port <port>: Set the port for the HTTP server. Defaults to 7000
-exclude <file>: Specify a file that lists data members that should
be excluded from the reachableFrom query.
-baseline <file>: Specify a baseline object dump. Objects in
both heap dumps with the same ID and same class will
be marked as not being "new".
-debug <int>: Set debug level.
0: No debug output
1: Debug hprof file parsing
2: Debug hprof file parsing, no server
-version Report version number
-h|-help Print this help and exit
<file> The file to read
For a dump file that contains multiple heap dumps,
you may specify which dump in the file
by appending "#<number>" to the file name, i.e. "foo.hprof#3".
All boolean options default to "true"
举例使用:
- step 1. 命令操作
Lin@****:~/tmp|⇒ jmap -dump:format=b,file=test.bin 99022
Dumping heap to /Users/Lin/tmp/test.bin ...
Heap dump file created
Lin@****:~/tmp|⇒ jhat test.bin
Reading from test.bin...
Dump file created Wed Feb 20 07:56:38 CST 2019
Snapshot read, resolving...
Resolving 21672 objects...
Chasing references, expect 4 dots....
Eliminating duplicate references....
Snapshot resolved.
Started HTTP server on port 7000
Server is ready.
- 浏览器打开 http://localhost:7000/
jstack: Java堆栈跟踪工具
jstack(Stack Trace for Java)(帮助文档)命令用于生成虚拟机当前时刻的线程快照(一般称为threaddump或者javacore文件)。线程快照就是当前虚拟机内每一条线程正在执行的方法堆栈的集合,生成线程快照的主要目的是定位线程出现长时间停顿的原因,如线程间死锁、死循环、请求外部资源导致的长时间等待等都是导致线程长时间停顿的常见原因。线程出现停顿的时候通过jstack来查看各个线程的调用堆栈,就可以知道没有响应的线程到底在后台做些什么事情,或者等待着什么资源。
命令语法格式:
Usage:
jstack [-l] <pid>
(to connect to running process)
jstack -F [-m] [-l] <pid>
(to connect to a hung process)
jstack [-m] [-l] <executable> <core>
(to connect to a core file)
jstack [-m] [-l] [server_id@]<remote server IP or hostname>
(to connect to a remote debug server)
Options:
-F to force a thread dump. Use when jstack <pid> does not respond (process is hung)
-m to print both java and native frames (mixed mode)
-l long listing. Prints additional information about locks
-h or -help to print this help message
jstack的主要选项:
举例使用:
Lin@***:~/tmp|⇒ jstack -l 99022
2019-02-20 08:02:55
Full thread dump Java HotSpot(TM) 64-Bit Server VM (25.74-b02 mixed mode):
"Attach Listener" #10 daemon prio=9 os_prio=31 tid=0x00007fb2aa872000 nid=0xe07 waiting on condition [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
Locked ownable synchronizers:
- None
"Monitor Ctrl-Break" #9 daemon prio=5 os_prio=31 tid=0x00007fb2aa86f800 nid=0x4503 runnable [0x000070000e987000]
java.lang.Thread.State: RUNNABLE
at java.net.PlainSocketImpl.socketAccept(Native Method)
at java.net.AbstractPlainSocketImpl.accept(AbstractPlainSocketImpl.java:409)
at java.net.ServerSocket.implAccept(ServerSocket.java:545)
at java.net.ServerSocket.accept(ServerSocket.java:513)
at com.intellij.rt.execution.application.AppMain$1.run(AppMain.java:90)
at java.lang.Thread.run(Thread.java:745)
Locked ownable synchronizers:
- None
"Service Thread" #8 daemon prio=9 os_prio=31 tid=0x00007fb2ab013000 nid=0x4703 runnable [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
...
.....
HSDIS:JIT生成代码反汇编
HSDIS 是一个Sun官方推荐的HotSpot虚拟机JIT编译代码的反汇编插件,它包含在HotSpot虚拟机的源码之中,但没有提供编译后的程序。
在Project Kenai的网站也可以下载到单独的源码。它的作用是让HotSpot的-XX:+PrintAssembly 指令调用它来把动态生成的本地代码还原为汇编代码输出,同时还生成了大量非常有价值的注释,这样我们就可以通过输出的代码来分析问题。
JDK的可视化工具
JDK中除了提供大量的命令行工具外,还有两个功能强大的可视化工具:JConsole和VisualVM,这两个工具是JDK的正式成员,没有被贴上“unsupported and experimental”的标签。
上述全面讲述了JDK命令行工具,可以方便地在服务器上直接分析JVM的状态,不过实话说,在实际工作中,我们需要使用可视化工具来查询、分析JVM情况,尤其对于不太会直接使用jhat这类的JVM堆栈快照分析,除非手上真的没有别的工具可用,一是因为分析工作是一个耗时而且消耗资源的事情,既然有可视化工具,就没必要受到命令行工具的限制了;二是命令行工具的分析比较简陋、不直观,后面介绍的VisualVM,以及专业用于分析dump文件的Eclipse Memory Analyzer、IBM HeapAnalyzer等工具有更强大更专业的分析功能。
JConsole:Java监视与管理控制台
从Java 5开始 引入了 JConsole,JConsole 是一个内置 Java 性能分析器。您可以轻松地使用 JConsole(或者,它更高端的 “近亲” jvisualvm )来监控 Java 应用程序性能和跟踪 Java 中的代码。(推荐使用升级版 JConsole 即 jvisualvm 。)
启动程序:
先看一下工具的界面:
JConsole主界面,可以看到主界面里共包括“概述”、“内存”、“线程”、“类”、“VM摘要”、“MBean”6个页签
接下来,笔者创建一个「MonitorTest」反面教材代码,来一一介绍JConsole的几个tab页。
- 内存监控
代码清单:
/**
* -Xms100m -Xmx100m -XX:+UseSerialGC
* 以64KB/50毫秒的速度往Java堆中填充数据,一共填充1000次
* Created by Lin on 2019/2/21.
*/
public class MonitorTest {
/**
* 内存占位符对象, 一个OOMObject大约占64KB
*/
static class OOMObject {
public byte[] placeholder = new byte[64 * 1024];
}
public static void fillHeap(int num) throws InterruptedException {
List<OOMObject> list = new ArrayList<>();
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);
}
}
观察内存Tab
内存tab相当于可视化的jstat命令,用于监视收集器管理的虚拟机内存(Java堆和永久代)的变化趋势。我们通过运行上述代码来体验一下它的监视功能:
运行时设置的虚拟机参数为: -Xms100m -Xmx100m -XX:+UseSerialGC,以64KM/50毫秒的速度往Java堆中填充数据,一共填充1000次,使用JConsole的「内存」tab进行监视,观察曲线和柱状指示图的变化。在「内存」tab中可以看到内存池Eden区的运行趋势呈现折线状,将监视范围扩大至整个堆后,会发现曲线是一条向上增长的平滑曲线,从柱状图看,1000次循环结束,整个新生代Eden和Survivor区都基本被清空了,但是老年代仍然保持峰值状态。下面两个小问题:
- 虚拟机启动参数只限制了Java堆为100MB,没有指定-Xmn参数,能否估计出新生代有多大?
- 为何执行了
System.gc();
之后,老年代的柱状图仍然显示峰值状态?
答案:
问题1: 图中显示Eden空间为27328KB,因为没有设置
-XX:SurvivorRadio
参数,所以Eden与Survivor空间比例默认为8:1,整个新生代空间大约为27328KB*125%=34160KB。问题2: 执行完System.gc()之后,空间未能回收是因为List<OOMObject>list对象仍然存活,fillHeap()方法仍然没有退出,因此list对象在System.gc()执行时仍然处于作用域之内[2]。如果把System.gc()移动到fillHeap()方法外调用就可以回收掉全部内存。
线程监控
「线程」tab的功能相当于可视化的jstack命令,遇到线程停顿时可以使用这个tab进行监控分析。线程长时间停顿的主要原因有:
- 等待外部资源(数据库连接、网络资源、设备资源等)
- 死循环
- 锁
- ...
线程等待的演示代码:
/**
* 线程死循环模拟
*/
public static void createBusyThread() {
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
while (true){
;
}
}
}, "testBusyThread");
thread.start();
}
public static void createLockThread(final Object lock) {
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
synchronized (lock) {
try {
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}, "testLockThread");
thread.start();
}
public static void main(String[] args) throws Exception{
Thread.sleep(5000);
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(System.in));
bufferedReader.readLine();
System.out.println("create busy thread.");
createBusyThread();
bufferedReader.readLine();
System.out.println("create lock thread.");
createLockThread(new Object());
bufferedReader.readLine();
}
运行上述代码,在「线程」tab中选择main线程,堆栈追踪显示BufferedReader在readBytes方法中等待System.in的键盘输入,线程状态是Runnable状态,Runnable状态的线程会被分配运行时间,但readBytes方法检查到流没有更新时会立刻归还执行令牌,这种等待只消耗很小的CPU资源。
接着监视testBusyThread线程,testBusyThread线程一直在执行空循环,从堆栈追踪中看到一直在MonitoringTest.java代码的41行停留,43行为:while(true)。这时候线程为Runnable状态,而且没有归还线程执行令牌的动作,会在空循环上用尽全部执行时间直到线程切换,这种等待会消耗较多的CPU资源。
下图显示testLockThread线程在等待着lock对象的notify或notifyAll方法的出现,线程这时候处于WAITING状态,在被唤醒前不会被分配执行时间。
testLockThread线程正在处于正常的活锁等待,只要lock对象的notify()或notifyAll()方法被调用,这个线程便能激活以继续执行。下面演示一个无法再被激活的死锁等待。
死锁的代码:
public class SynAddRunnable implements Runnable {
int a, b;
SynAddRunnable(int a, int b) {
this.a = a;
this.b = b;
}
@Override
public void run() {
synchronized (Integer.valueOf(a)) {
synchronized ((Integer.valueOf(b))) {
System.out.println(a + b);
}
}
}
public static void main(String[] args) {
System.out.println("lock thread start");
for (int i = 0; i < 100; i++) {
new Thread(new SynAddRunnable(1, 2)).start();
new Thread(new SynAddRunnable(2, 1)).start();
}
}
}
这段代码开了200个线程去分别计算1+2以及2+1的值,其实for循环是可省略的,两个线程也可能会导致死锁,不过那样概率太小,需要尝试运行很多次才能看到效果。一般的话,带for循环的版本最多运行2~3次就会遇到线程死锁,程序无法结束。
出现线程死锁之后,点击JConsole线程面板的“检测到死锁”按钮,将出现一个新的“死锁”页签,如图:
很清晰地显示了线程Thread-35在等待一个被线程Thread-36持有Integer对象,而点击线程Thread-36则显示它也在等待一个Integer对象,被线程Thread-35持有,这样两个线程就互相卡住,都不存在等到锁释放的希望了。
4.3.2 VisualVM:多合一故障处理工具
VisualVM(All-in-One Java Troubleshooting Tool)是到目前为止随JDK发布的功能最强大的运行监视和故障处理程序,并且可以预见在未来一段时间内都是官方主力发展的虚拟机故障处理工具。
下面演习下如何使用VisualVM进行JAVA性能分析及调优。
- Mac环境安装JVisualVM:
安装不多说,请前往官网自行下载安装。
- 启动JVisualVM
终端命令: jvisualvm
- 插件
首次启动VisualVM后,读者先不必着急找应用程序进行监测,因为现在VisualVM还没有加载任何插件,虽然基本的监视、线程面板的功能主程序都以默认插件的形式提供了,但是不给VisualVM装任何扩展插件,就相当于放弃了它最精华的功能,和没有安装任何应用软件操作系统差不多。
目前VisualVM已经由 visualvm.java.net 迁移到了 visualvm.github.io,因此如果你使用JDK下的jvisualvm,当你安装插件时会失败,你可以Tools -> Plugins -> Settings添加最新的插件中心如:https://visualvm.github.io/uc/release139/updates.xml.gz
各版本最新的插件中心可以这里查看:https://visualvm.github.io/pluginscenters.html
- JVisualVM会自动检测本地的java应用,使用「Visual GC」
通过新增最新的插件中心后,你可以看到「可用插件」中多了Tools,其中有「Visual GC」,推荐安装。
下面来监控之前的「MonitorTest」
调整System.gc();
到fillHeap(1000);
之外后,
相比jconsole,漂亮多了。
课后作业
经典面试问题: 如何监控和诊断JVM堆内和堆外内存使用?
参考
深入理解Java虚拟机:JVM高级特性与最佳实践(第2版)