文章目录
- 命令行工具
- jps(JVM process Status Tool)
- jps失效问题
- jstat(JVM statistics Monitoring Tool)
- jinfo
- jmap
- jhat
- OQL
- jstack
- 可视化工具
- jhsdb
- jconsole
- 空循环
- 活锁
- 死锁
- jvisualvm
- 插件下载
- 生成堆快照
- 保存以及导入
- 分析CPU和内存
- BTrace动态日志跟踪
- 代码清单
- JMC
- HotSpot虚拟机插件及工具
- HSDIS:JIT生成代码反汇编
给一个系统定位问题的时候,知识、经验是关键基础,数据是依据,工具是运用知识处理数据的手段
具体功能都在tools.jar中
命令行工具
jps(JVM process Status Tool)
虚拟机进程状况工具
列出正在运行的虚拟机进程,
显示虚拟机执行主类(Main Class,main()函数所在类)的名称,
进程在本地虚拟机的唯一ID(LVMID local Virtual Machine Identifier)
通过RMI协议查询开启RMI服务的远程虚拟机进程状态
LVMID与操作系统的进程id(PID,Process Identifier)一致
jps失效问题
java程序启动后,会在目录/tmp/hsperfdata_{userName}/下生成几个文件,文件名就是java进程的pid,因此jps列出进程id就是把这个目录下的文件名列一下而已,至于系统参数,则是读取文件中的内容。
我们来思考下:如果由于磁盘满了,无法创建这些文件,或者用户对这些文件没哟读的权限。又或者因为某种原因这些文件或者目录被清除,出现以上这些情况,就会导致jps命令失效。
如果jps命令失效,而我们又要获取pid,还可以使用以下两种方法:
1、top | grep java
2、ps -ef |grep java
jstat(JVM statistics Monitoring Tool)
监视虚拟机各种运行状态信息工具
显示本地或远程虚拟机进程中的类装载,内存,垃圾收集,JIT编译等运行数据
命令格式
jstat[option vimd [interval[s|ms][count]]]
本地虚拟机进程vmid和lvmid一致
远程虚拟机进程,vmid格式为[protocol:][//]lvmid[@hostname[:port]/servername]
interval与count表示:查询间隔和次数,省略默认查询一次
opt:用户希望查询的虚拟机信息,类装载,垃圾收集,运行期编译状况
S0和S1表示2个Survivor区
O:老年代
P:永久代
YGC:新生代GC次数
FGC:full GC次数
FGCT:full GC总耗时
GCT:GC总耗时
jinfo
java配置信息工具
实时查看和调整虚拟机的各项参数
jps -v:查看虚拟机启动时显示指定的参数列表
-flag:未被显式指定的参数的系统默认值(1.6及以上,使用java -XX:+PrintFlagsFinal也可以)
-sysprops:打印虚拟机进程的System.getProperties()
JDK1.6之后:-flag [+|-]name或者 -flag name=value修改一部分运行期间可写的虚拟机参数值
命令
jinfo [option] pid
jmap
java内存映像工具
用于生成堆转换快照(一般称之为heapdump/dump文件),查询finalize执行队列,java堆和永久代的详细信息(如空间使用率,当前用的是那种收集器等)
也可以使用-XX:+HeapDumpOnOutOfMemoryError参数:虚拟机在OOM异常出现之后自动生成dump文件
-XX:+HeapDumpOnCtrlBreak:可以使用Ctrl+Break键让虚拟机生成dump文件,
Linux可以使用Kill -3发出进程退出信号,生成dump文件
-dump/-histo只能在window使用,其余选项,只能在linux/solaris使用
命令格式
jmap [option] vmid
jmap -dump:format=b,file=a 7664
jhat
分析jmap生成的堆转储快照
内置卫星的HTTP/HTML服务器,生成的dump文件分析结果后可以在浏览器中查看,分析结果默认包单位分组
http://localhost:7000/
局限,分析耗时且消耗硬件资源,功能过于简陋
命令格式
jhat dump文件路径
OQL
类似于SQL的查询语言,用于查询Java堆。
jstack
java堆栈跟踪工具
生成虚拟机当前时刻的线程快照(一般称之为threaddump/javacore文件)
线程快照就是当前虚拟机内每一条线程正在执行的堆栈的集合
线程快照主要目的定位线程出现长时间停顿的原因(如线程间死锁,死循环..)
命令格式
jstack [option] vmid
java.lang.Thread#getAllStackTraces()用于获取虚拟机中所有线程的StackTraceElement对象,可以完成jstack大部分功能
可视化工具
jhsdb
/**
* -Xmx10m -XX:+UseSerialGC -XX:-UseCompressedOops
* 由于JHSDB本身对压缩指针的支持存在很多缺陷,建议用64位系统的读者在实验时禁用压缩指针(-XX:-UseCompressedOops)
*/
public class JHSDB_TestCase {
static class Test {
static ObjectHolder staticObj = new ObjectHolder();//方法区,元空间,对象存储堆中
ObjectHolder instanceObj = new ObjectHolder(); //堆中
void foo() {
ObjectHolder localObj = new ObjectHolder();//foo局部变量表栈中存索引,对象存储堆中
System.out.println("done"); // 这里设一个断点
}
}
private static class ObjectHolder {
}
public static void main(String[] args) {
Test test = new JHSDB_TestCase.Test();
test.foo();
}
}
在java9之前,JAVA_HOME/lib目录下有个sa-jdi.jar,可以通过如上命令启动HSDB(图形界面)及CLHSDB(命令行)
sa-jdi.jar中的sa的全称为Serviceability Agent,它之前是sun公司提供的一个用于协助调试HotSpot的组件,而HSDB便是使用Serviceability Agent来实现的
HSDB就是HotSpot Debugger的简称,由于Serviceability Agent在使用的时候会先attach进程,然后暂停进程进行snapshot,最后deattach进程(进程恢复运行),所以在使用HSDB时要注意
启动图形页面
java -cp $JAVA_HOME/lib/sa-jdi.jar sun.jvm.hotspot.HSDB
启动命令行
java -cp $JAVA_HOME/lib/sa-jdi.jar sun.jvm.hotspot.CLHSDB
使用scanoops命令在Java堆的新生代(从Eden起始地址到To Survivor结束地址)范围内查找ObjectHolder的实例
命令行:scanoops 0x04a00000 0x04f50000 C_JConsoleAndVisualVM.JHSDB_TestCase$ObjectHolder
发现有三个ObjectHolder对象,三个对象分别存储在堆中,
staticObj,instanceObj存储堆中,localObj存储局部变量表(栈中)通过Inspector查看存放对象信息
可以查看到对象头和指向对象元数据的指针,里面包括了Java类型的名字、继承关系、实现接口关系,字段信息、方法信息、运行时常量池的指针、内嵌的虚方法表(vtable)以及接口方法表(itable)等。
revptrs 0x04c1fd50 0x04c1d1c0
revptrs 0x04c1fd68 0x04c1fd58
revptrs 0x04c1fd70
可以看到0x04c1fd50由0x04c1d1c0引用
下图也看得出来
当查看0x04c1fd70引用的时候发现null,也就是说不在堆中,刚测的2个在堆中的对象分别是staticObj,instanceObj,也就是说localObj的引用不在堆,它的引用在局部变量表栈中,从栈中查找就行了
jdk8这里显示的是局部变量没有找到引用,无法测试了,正常的显示应该是在
jconsole
空循环
可看见线程处于RUNNABLE运行状态,这种情况会一直占用CPU
活锁
死锁
jvisualvm
插件下载
点击工具->插件
下载插件会jdk1.6\lib\visualvm放在此目录下
生成堆快照
保存以及导入
dump文件保存,创建dump文件后,右键-->另存为,不这样的话关闭visualVM时默认当做临时文件处理掉
导入:点击window视图-->文件-->装入-->选择文件
分析CPU和内存
注意:jdk1.5之后,在client模式下的虚拟机加入并且自动开启了类共享,这是一个在多虚拟机进程中共享rt.jar中的类数据以提高加载速度和节省内存的优化
VisualVM的Profiler功能可能会因为类共享而导致被监视的应用程序崩溃,建立关闭类共享优化-Xshare:off
BTrace动态日志跟踪
在不停止目标程序运行的前提下,通过HotSpot虚拟机的HotSwap技术动态加入原本并不存在的调试代码。
范围使用:排除错误,如方法参数,返回值
1:下载BTrace插件
2: 点击应用程序面板右键,点击Trace Application
代码清单
public class D_testVisualVM {
public int add(int a, int b) {
return a + b;
}
public static void main(String[] args) throws Exception {
D_testVisualVM b_Trace = new D_testVisualVM();
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
for (int i = 0; i < 10; i++) {
br.readLine();
int a = (int) (Math.random() * 1000);
int b = (int) (Math.random() * 1000);
System.out.println(b_Trace.add(a, b));
}
}
}
日志代码
import com.sun.btrace.annotations.*;
import static com.sun.btrace.BTraceUtils.*;
@BTrace
public class TracingScript {
@OnMethod(
clazz="C_JConsoleAndVisualVM.D_testVisualVM",
method="add",
location=@Location(Kind.RETURN)
)
public static void func(@Self C_JConsoleAndVisualVM.D_testVisualVM instance,int a,int b ,@Return int result){
jstack();
println(strcat("方法参数A:",str(a)));
println(strcat("方法参数B:",str(b)));
println(strcat("方法结果:",str(result)));
}
}
在运行期间动态织入日志代码,跟idea,debug时可以动态设置步入条件语句,还有Fiddler抓websocket织入语句一样.
JMC
Java Mission Control:可持续在线的监控工具
JFR的基本工作逻辑是开启一系列事件的录制动作,当某个事件发生时,这个事件的所有上下文
数据将会以循环日志的形式被保存至内存或者指定的某个文件当中,循环日志相当于数据流被保留在
一个环形缓存中,所以只有最近发生的事件的数据才是可用的。JMC从虚拟机内存或者文件中读取并
展示这些事件数据,并通过这些数据进行性能分析。
HotSpot虚拟机插件及工具
·Ideal Graph Visualizer:用于可视化展示C2即时编译器是如何将字节码转化为理想图,然后转化为
机器码的。
·Client Compiler Visualizer[1]:用于查看C1即时编译器生成高级中间表示(HIR),转换成低级中
间表示(LIR)和做物理寄存器分配的过程。
·MakeDeps:帮助处理HotSpot的编译依赖的工具。
·Project Creator:帮忙生成Visual Studio的.project文件的工具。
·LogCompilation:将-XX:+LogCompilation输出的日志整理成更容易阅读的格式的工具。
·HSDIS:即时编译器的反汇编插件。
HSDIS:JIT生成代码反汇编
让HotSpot的-XX:+PrintAssembly指令调用它来把即时编译器动态生成的本
地代码还原为汇编代码输出,同时还会自动产生大量非常有价值的注释
使用的是SlowDebug或者FastDebug版的HotSpot,那可以直接通
过-XX:+PrintAssembly指令使用的插件;如果使用的是Product版的HotSpot,则还要额外加入一
个-XX:+UnlockDiagnosticVMOptions参数才可以工作
命令
java -XX:+PrintAssembly -Xcomp -XX:CompileCommand=dontinline,*Bar.sum -XX:Compile-Command=compileonly,*Ba
参数-Xcomp是让虚拟机以编译模式执行代码,这样不需要执行足够次数来预热就能触发即
时编译。两个-XX:CompileCommand的意思是让编译器不要内联sum()并且只编译sum(),-XX:
+PrintAssembly就是输出反汇编内容
当汇编代码量过大的时候,可使用JITWatch
java -XX:+PrintAssembly -XX:+UnlockDiagnosticVMOptions -Xcomp -XX:CompileCommand=dontinline,*Bar.sum -XX:Compile-Command=compileonly,*B