深入理解java虚拟机 摘要
二、虚拟机性能监控与故障处理工具
1. JDK的可视化工具
(一)JConsole:Java监视与管理控制台
JConsole(Java Monitoring and Management Console)是一种基于JMX的可视化监视、管理工具。它管理部分的功能是针对JMX MBean进行管理,由于MBean可以使用代码、中间件服务器的管理控制台或者所有符合JMX规范的软件进行访问
1. 启动JConsole
通过JDK/bin目录下的“jconsole.exe”启动JConsole后,将自动搜索出本机运行的所有虚拟机进程,不需要用户自己再使用jps来查询了。双击选择其中一个进程即可开始监控,也可以使用下面的“远程进程”功能来连接远程服务器,对远程虚拟机进行监控。
- 内存监控
“内存”页签相当于可视化的jstat命令,用于监视受收集器管理的虚拟机内存(Java堆和永久代)的变化趋势。我们通过运行代码来体验一下它的监视功能。运行时设置的虚拟机参数为:-Xms100m-Xmx100m-XX:+UseSerialGC,这段代码的作用是以10KB/50毫秒的速度往Java堆中填充数据,一共填充1000次,使用JConsole的“内存”页签进行监视,观察曲线和柱状指示图的变化
代码示例:
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;
/**
* 堆内存增长模拟
*
* -Xms100m -Xmx100m -XX:+UseSerialGC
* @author wp
* @create 2018-02-26 14:31
*
* 输入:
* begin 开启任务
* exit 退出
**/
public class HeapIncrease {
public static void fillHeap(Integer num) {
List<OomObject> list = new ArrayList<OomObject>();
for (int i = 0; i < num; i++) {
try {
Thread.sleep(50);
list.add(new OomObject());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static void main(String args[]){
Scanner scanner = new Scanner(System.in);
while (true){
String flag = scanner.next();
if(flag.equals("begin")){
fillHeap(1000);
System.out.println("over!!!");
}else if(flag.equals("exit")){
System.out.println("over!!!");
break;
}
}
}
}
class OomObject {
Byte[] bytes = new Byte[10 * 1024];
}
- 线程监控
“线程”页签的功能相当于可视化的jstack命令,遇到线程停顿时可以使用这个页签进行监控分析。
代码示例:
/**
* 线程异常演示
*
* @author wp
* @create 2018-03-01 15:51
* 输入:
* busyThread 创建一条死循环线程
* lockThread 创建一条锁等待线程
* deadLockThread 创建200条线程 以产生死锁
* exit 退出
**/
public class ThreadErr {
/**
* 线程死循环演示
*/
public static void createBusyThread(String name) {
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
while (true) {
}
}
}, name);
thread.start();
}
/**
* 线程锁等待演示
*/
public static void createLockThread(final Object lock,String name) {
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
synchronized (lock) {
try {
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}, name);
thread.start();
}
/**
*线程死锁等待演示
*/
static class SynAddRunalbe implements Runnable{
int a,b;
public SynAddRunalbe(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) throws Exception {
Scanner scanner = new Scanner(System.in);
int i =1;
out:
while (true){
String command = scanner.nextLine();
switch (command){
case "busyThread":
createBusyThread("busyThread -"+i);
break;
case "lockThread":
createLockThread(new Object(),"lockThread -" + i);
break;
case "deadLockThread":
for (int j = 0; j < 100; j++) {
new Thread(new SynAddRunalbe(1, 2),"deadLockThread -"+i).start();
new Thread(new SynAddRunalbe(2,1),"deadLockThread -"+i).start();
}
break;
case "exit":
break out;
}
i++;
}
}
}
死锁产生解释:
这段代码开了200个线程去分别计算1+2以及2+1的值,其实for循环是可省略的,两个线程也可能会导致死锁,不过那样概率太小,需要尝试运行很多次才能看到效果。一般的话,带for循环的版本最多运行2~3次就会遇到线程死锁,程序无法结束。造成死锁的原因是
Integer.valueOf()方法基于减少对象创建次数和节省内存的考虑,[-128,127]之间的数字会被缓存 [3] ,当valueOf()方法传入参数在这个范围之内,将直接返回缓存中的对象。也就是说,代码中调用了200次Integer.valueOf()方法一共就只返回了两个不同的对象。假如在某个线程的两个synchronized块之间发生了一次线程切换,那就会出现线程A等着被线程B持有的Integer.valueOf(1),线程B又等着被线程A持有的Integer.valueOf(2),结果出现大家都跑不下去的情景。
PS:运行结果不多做解释 , 大家自行观察
(二)VisualVM:多合一故障处理工具
VisualVM(All-in-One Java Troubleshooting Tool)是到目前为止随JDK发布的功能最强大的运行监视和故障处理程序,并且可以预见在未来一段时间内都是官方主力发展的虚拟机故障处理工具。官方在VisualVM的软件说明中写上了“All-in-One”的描述字样,预示着它除了运行监视、故障处理外,还提供了很多其他方面的功能。
位置:位于jdk,bin目录下的 jvisualvm.exe
官网:https://visualvm.github.io/index.html
1. 插件
插件可以进行手工安装,在相关网站上下载*.nbm包后,点击“工具”→“插件”→“已下载”菜单,然后在弹出的对话框中指定nbm包路径便可进行安装,插件安装后存放在
JDK_HOME/lib/visualvm/visualvm中。不过手工安装并不常用,使用VisualVM的自动安装功能已经可以找到大多数所需的插件,在有网络连接的环境下,点击“工具”→“插件菜单”,大家可以根据自己的工作需要和兴趣选择合适的插件,然后点击安装按钮。
2. 生成、浏览堆转储快照
3. 分析程序性能
在Profiler页签中,VisualVM提供了程序运行期间方法级的CPU执行时间分析以及内存分析,做Profiling分析肯定会对程序运行性能有比较大的影响,所以一般不在生产环境中使用这项功能。要开始分析,先选择“CPU”和“内存”按钮中的一个,然后切换到应用程序中对程序进行操作,VisualVM会记录到这段时间中应用程序执行过的方法。如果是CPU分析,将会统计每个方法的执行次数、执行耗时;如果是内存分析,则会统计每个方法关联的对象数以及这些对象所占的空间。分析结束后,点击“停止”按钮结束监控过程
4. BTrace动态日志跟踪
BTrace 是一个很“有趣”的VisualVM插件,本身也是可以独立运行的程序。它的作用是在不停止目标程序运行的前提下,通过HotSpot虚拟机的HotSwap技术动态加入原本并不存在的调试代码。
在VisualVM中安装了BTrace插件后,在应用程序面板中右键点击要调试的程序,会出现“Trace Application……”菜单,点击将进入BTrace面板。
BTrace的用法有许多,打印调用堆栈、参数、返回值只是最基本的应用,在它的网站上有使用BTrace进行性能监视、定位连接泄漏和内存泄漏、解决多线程竞争问题等例子