通过《深入理解Java虚拟机》一书中,需要了很多,今天是对性能工具和故障处理的学习笔记:
在给系统定位问题的时候,知识,经验是关键基础,数据是依据,工具是处理数据的手段。此处所说的数据包括:GC日志,运行日志,异常堆栈,线程快照(Threaddump/javacore文件),
堆转储快照(heapdump/hprof文件)等。经常使用虚拟机监控和分析工具可以加快分析数据、定位解决问题的速度。
1)Java的命令行工具:bin下的Java.ext, javac.exe是运行和编译工具,几乎所有的体积都非常小,因为大多是类库中的一层薄包装,linux中的JDK,很多都是用shell脚本直接写成的
jdk 的故障和监控工具如下所示:
jps:虚拟机进程状况工具
是使用命令最高的JDK命令行工具,因为其他的JDK工具需要输入的条件LVMID需要这个命令查询出来,来确定要监控的进程是哪一个虚拟机进程。
jstat:虚拟机统计监控信息工具
用于监控虚拟机各个运行状态信息的命令行工具。它可以显示本地或者远程上的类装载,内存,垃圾收集,JIT编译等运行时数据。在没有GUI图像界面时,只提供纯文本控制台环境的服务器上,它将是定位运行期虚拟机性能问题的首选工具。
例如:jstat gc 2764 250 20: 表示需要250毫秒查询一次进程2764垃圾收集情况,一共查询20次
gc的位置可以换做其他的字段
jmap:Java内存映像工具,用于生成堆转储快照,一般成dump文件或者heapdump文件,还可以通过配置VM参数获取:-XXHeapDumpOnOutOfMemoryError
例如:jmap -dump:format=b,file=eclipse.bin 3500 生成一个正在运行的eclipse的dump快照文件的例子,后面的3500用于标识进程,可通过jps查询得知
jhat:虚拟机转储快照分析工具,与jmap搭配使用,结果分析后,可以在网页上查看具体情况,不过一般不这么做,因为分析工具是一种耗时耗资源的进程,可放到其他机器上运行,且分析功能简陋,再次不再叙述。
jstack:Java堆栈跟踪工具,用于生成当前时刻的线程快照,目的是定位线程出现长时间停顿的原因,如线程间的死锁,死循环,请求外部资源导致长时间停顿的原因。
线程停顿的时候,通过jstack查看各个线程的调用堆栈,就可以知道没有响应的线程到底在后台做些什么事情,或者在等待什么资源。
jstack [option] vmid :option处有3中情况:
1)F:当正常输入的请求不被响应时,强制输出线程堆栈
2)-l:除堆栈外,显示关于锁的附加信息
3)-m:如果调用到本地方法的话,可以显示C/C++的堆栈
JConsole:JDK的可视化工具,自动搜索出本机运行的所有虚拟机进程,不需要用户自己再使用jps来查询了,对远程虚拟机进程监控。概览页有4部分,堆内存使用情况,线程,类,CPU使用情况。概览是后面页的概述情况。内存页相当与可视化的jstat命令,用于监视收集器管理的虚拟机内存的变化趋势。
下面针对内存页的监视案例:
import java.util.ArrayList; import java.util.List; /** * 内存占位符对象,一个Object对象大约占64K * 虚拟机的参数设置为:-Xms100m -Xmx100m -XX:+UseSerialGC */ public class OOMObject { public byte[] placeholder=new byte[64*1024]; public static void fileHeap(int num)throws InterruptedException{ List<OOMObject> list=new ArrayList<OOMObject>(); for(int i=0;i<num;i++) { //稍作延时,令监视曲线的变化更加明显 Thread.sleep(50); list.add(new OOMObject()); } System.gc(); } public static void main(String[] args) throws Exception { fileHeap(1000); } }
运行bin下的jconsole.exe,会出现
选择你需要的程序文件,比如OOMObject类,在运行时,就会在下拉框中出现这个类的选项,截图如下所示;
下拉框可以选择想看的内存区域,Eden Space 区域是呈折线状的,扩大到整个堆后,是一条向上增长的曲线,老年代的圆柱仍然保持峰值,说明被填充进堆的数据在System.gc()后仍然存活,从这监控图里面,可以看出,Eden大小为27328KB,因为没有设置-XXSurvivorRadio(设置survivor与Eden的比值)参数,采用的默认比例Eden:survivor=8:1.
则整个新生代空间大约为27328*125%=34160KB。
线程监控:
线程长时间等待的原因:等待外部资源(数据库连接,网络资源,设备资源等),死循环,所等待(死锁和活锁)。
线程等待的演示实例代码如下所示:
package com.three.twentynine;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
public class TestThread {
//线程死循环方法
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) {//此处的synchronized用来实现加锁机制,只有一个线程执行锁住的代码,其他线程需要等待
try {
lock.wait(); //这个是一直放弃锁资源,需要唤醒才能继续获取锁然后运行
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}},"testLockThread") ;
thread.start();
}
public static void main(String[] args) throws Exception {
BufferedReader br=new BufferedReader(new InputStreamReader(System.in));
br.readLine();
createBusyThread();
br.readLine();
}
}
在运行后,在线程中选择main查看:截图如下所示:
接着选择死循环方法,会看到,此循环一直在执行空循环,从堆栈中可以看到哪个类中的哪行代码中停留,而且没有看到线程归还执行令牌的动作,会一直执行直到线程切换,这种等待会消耗较多的CPU资源。截图如下所示;
划横线的地方就是具体哪个类中的哪个方法,在哪行出的问题,这个是说TestThread.java类中的第16行代码,就是while(true);这行代码。
而线程锁等待部分,testLockThread线程在等待着lock对象的notify,notifyall方法,线程这时就是waiting状态,在被唤醒前不会被分配执行时间。