JDK的bin目录下那些常见工具使用集锦
在JDK的bin目录下有许多java小工具可以用于编译,运行以及调试Java程序或监控JVM的运行。在习惯于在Window下使用Eclipse等IDE工具开发的程序员来说,可能bin目录下的很多工具都很少使用。但是如果是运行在Linux下的应用程序并且是命令行模式运行的话,很多时候使用JDK提供的一些工具可以给开发人员带来很多便利。本文总结了在实际开发中我们可能接触到或需要掌握的一些小工具的基本用法。对于像java,javac,javadoc这样的工具可能更多的时候不需要我们掌握,因此本文也只是提供基本用法。但是对于像jps,jdb等这样一些debug或监控工具,对于在linux下的命令行模式下调试以及监控Java应用程序很有用,因此重点介绍。
注意:所有的这些工具的使用的前提是已经配置和Path环境变量
1. javac
很多第一次接触java的开发者写得第一个helloworld程序使用的第一个编译器估计就是
javac
了。这是JDK提供的命令行下java程序编译工具。语法如下:
javac [ options ] [ sourcefiles ] [ classes ] [ @argfiles ]
最简单的用法是:
javac 'your sourcefile name' 'your class filename'
比如我编译HelloWorld.java这个文件
javac HelloWorld.java
这样就会在这个源文件目录下生成一个HelloWorld.class文件,我们也可以一次编译多个,语法是:
javac source1.java source2.java source3.java
实际开发中,文件数量,包数量已经文件关系比这个复杂得多。而且我们希望生成的class文件和源文件是分开的。这就需要使用到这个工具的选项了。
想要javac有哪些选项可以用,最简单的方式就是输入:
javac -help
事实上,作为一般开发者,根本不需要使用javac,他们只要将写好的源码提交到svn,会有构建工具自动打包编译测试发布的
实际开发中,假如要用到这个工具,记的
javac -help
2. java
java
工具用于启动一个java应用程序,这个java应用程序必须要与入口函数main方法。这个工具的语法是
java [ options ] -jar file.jar [ arguments ]
我们也可以通过
java -help
来查看这个工具的选项和用法。
3. jar
jar
这个工具用于打包、更新、解包java应用程序。实际开发中,我们都会将应用程序打包成一个jar包来运行,jar会将全部class文件以及资源文件,配置文件放到一个文件里打个包(有的说是压缩,事实上这个工具仅仅是打包,并不包含压缩操作)。创建一个jar文件常用语法如下:
jar c[v0M]f jarfile [-C dir] inputfiles [-Joption]
jar c[v0]mf manifest jarfile [-C dir] inputfiles [-Joption] [-e entrypoint]
jar c[v0M] [-C dir] inputfiles [-Joption]
jar c[v0]m manifest [-C dir] inputfiles [-Joption]
这里的
c
就是创建的意思,jarfile是要打包的jar文件名,-C临时改变输入文件源,-J指定运行参数,比如指定初始堆栈和最大堆栈大小等。参数和-J之间不能含有空格。实际开发中,这一步骤也是写在脚本中,一般不需要手动调用。
4. javadoc
javadoc
是JDK提供给程序员的一个文档生成工具。这个工具我觉得是java比C/C++在文档化方面先进的地方。只要按照正确的格式在代码中写上对应的注释,程序写完后,使用这个工具就可以自动生成HTML样式的文档。比如JDK API的文档就是使用这个工具生成的。要生成类似文档的注释语法可以参考链接中的文档或直接看看JDK中的注释。这个工具的基本语法是:
javadoc [ options ] [ packagenames ] [ sourcefilenames ] [ -subpackages pkg1:pkg2:... ] [ @argfiles ]
这个工具只会将”*.java”中的注释文档化,我们需要指定包名和源文件名,如果有子包也需要特别指出。需要注意的是这个工具不支持通配符。详细的语法以及用法可以参考这个
文档
。
5.javap
javap
是一个反编译工具,也可以查看class文件中的信息。基本语法如下:
javap [ options ] classes
如果不带任何参数,则只会将class文件中的包信息,protected和public字段以及方法打印出来。比如我们有一个这样的HelloWord.java文件:
public class HelloWorld {
public String pubStr = "public string";
private String priStr = "private string";
protected String proStr = "protected string";
public static void main(String[] args) {
sayHello("JackWang");
}
private static void sayHello(String name) {
System.out.println("Hello," + name);
}
public void pubMethod() {
System.out.println("hello");
}
}
在生成的class文件目录下使用
javap HelloWorld.class
生成的信息如下:
如果要查看编译后的指令,可以加一个 -c 选项:
javap -c HelloWorld.class
生成的信息如下:
一次也可以反编译多个class文件,只要将class文件名加在后面即可。如果只要打印这个类的public字段和方法,可以这样:
javap -public HelloWorld.class
同样如果是
-private
或
-p
则会打印包括 private在内的所有可见字段或方法,也就是全部,如果是
-protected
,则打印protected和public的,如果是
-package
则打印无可见符修饰以及protected,public的。
如果要打印这个类的MD5,最后修改时间等元数据,则可以加一个 -sysinfo参数:
javap -sysinfo HelloWorld.class
生成的信息如下:
其他参数可以参考javap文档或输入
javap
参考帮助信息。
6.jdb
jdb
是一个断点工具,类似于gdb。基本语法如下:
jdb [ options ] [ class ] [ arguments ]
IDE的断点可以本地调试生产环境代码,但是却不能在线调试,这时候就可以使用jdb了。总的来说,jdb的使用比gdb复杂一些。如果用过gdb,则入手jdb相对会简单一些。
首先,我们的应用程序启动脚本要配置了如下的启动参数:
JAVA_OPTS="$JAVA_OPTS -Xdebug -Xrunjdwp:transport=dt_socket,address=8787,server=y,suspend=y"
然后启动程序,在程序运行所在机器上输入一下命令:
jdb -attach 127.0.0.1:8787
如果前面配置了启动参数,则会进入jdb模式:
这里我使用了Tomcat 7.0.67这个版本作为测试,在Bootstrap这个启动类的396行打了一个断点
stop at org.apache.catalina.startup.Bootstrap:396
如果我们要在某个方法执行前打一个断点,则可以使用
stop at org.apache.catalina.startup.Bootstrap.main
这样就在main方法上打了一个断点。如果某个方法被重载,则还要指定相应的参数。对应的清楚断点是
clear org.apache.catalina.startup.Bootstrap:396
然后输入
run
让程序跑起来,则自动会在396行断住。
输入
next
则执行到下一行,相当于Eclipse中的F6,输入
step
则跳进方法,相当于Eclipse中的F5
使用
print
或
dump
可以将一个变量或对象的值打印出来
输入
threads
可以查看当前这个JVM运行的全部线程
其他详细命令可以参考
文档
7.jps
jps
用于查看运行的JVM实例以及进程号。我们常常使用jps工具开查看某一进程的进程号然后使用其他工具来监控这一进程的运行信息。基本语法如下:
jps [ options ] [ hostid ]
如果是查看本机运行的JVM实例,直接输入
jps
如果要查看运行的JVM的main方法传入的参数可以输入
jps -m
如果要查看运行的JVM的各个参数,可以输入
jps -v
如果要只要查看运行的进程ID,可以输入
jps -q
如果要查看运行的JVM的全限定名,可以输入
jps -l
8.jstat
jstat
用于查看运行的JVM实例的运行数据。基本语法如下:
jstat [ generalOption | outputOptions vmid [interval[s|ms] [count]] ]
其中,vmid需要通过jps来获取。可以通过输入各个选项来查看相应查看的运行数据。
比如我想查看29869这个进程的类加载信息,每1秒查看一次,总共查看3次,可以输入
jstat -class 29869 1000 3
得到的数据如下:
Loaded一列表示已经加载完成的类,总共加载了16183.3KB的数据,Unloaded一列表示已经卸载的类,总共卸载了110.0KB的数据,加载和卸载耗时17.16个时间单位(秒还是毫秒我也不确定)。
这里如果我不指定打印3次,则每秒钟都会打印一次,一直打印下去。如果不指定频率,则只会打印查询那一刻的数据。
如果想查看编译信息,则输入
jstat -compiler 29869
Compiled表示编译成功的类数目,其他各列可以根据名字猜测出来,也可以查看
jstat
文档
如果输入
jstat -gc 29869 1000 3
则表示查看29869这个进程的堆内存回收情况,每秒查看一次,总共查看3次。得到的信息如下:
这里S0C一列表示新生代幸存0区已经占用了75264KB内存,
S1C一列表示幸存1区已经占用74240KB内存,
S0U表示幸存0区还有1408KB的内存可用,
S1U表示幸存1区还有0KB的内存可用,
EC表示当前新生代已经用了200192KB的内存,
EU表示当前新生代还有139540.9KB内存可用,
OC表示当前老年代已经用了699392KB内存,
OU表示当前老年代还有561520.8KB内存可用,
PC表示当前永久代已经用了48128KB内存,
PU表示当前永久代还有48078.4KB内存可用,
YGC表示到目前为止总共发生了72828次新生代垃圾回收(MinGC),
YGCT表示到目前为止新生代垃圾回收耗时2872.334个时间单位(秒还是毫秒?),
FGC表示到目前为止总共发生了2659次full GC,
FGCT表示Full GC耗时2031.945,
GCT表示总共的垃圾回收耗时为4904.279,也就是MinGC Time + Full GC Time
如果输入
jstat -gccapacity 29869 1000 3
则获得的信息如下:
这个获得数据是这个JVM实例内存配置数据,
NGCMN表示最小新生代内存为349696KB,可以在程序启动时通过JVM参数指定(-xmn),
NGCMX表示最大新生代内存为349696KB,
NGC表示当前新生代内存占用了349696KB,
S0C表示当前幸存0区大小为76288KB,
S1C表示当前幸存1区大小为74752KB,
EC表示当前新生代空间为198656KB,
OGCMN表示最小老年代内存为699392KB,
OGCMX表示最大老年代内存为699392KB,
OGC表示已经占用的老年代内存为699392KB,
OC表示当前老年代内存为699392KB,
PGCMN和PGCMX以及PGC,PC表示永久代,意义类似。
YGC表示发生minGC的次数,FGC表示发生Full GC的次数。
如果输入
jstat -gccause 29869 1000 3
则获得的是垃圾回收的统计数据。
S0表示幸存0区占用百分比,
S1表示幸存1区占用百分比,
E表示新生代占用百分比
O表示老年代占用百分比
P表示永久代占用百分比
YGC表示minGC发生次数
YGCT表示MinGC发生耗时
FGC表示Full GC发生次数
FGCT表示FullGC耗时
GCT表示总共垃圾回收耗时
LGCC表示造成上一次垃圾回收原因,GCC表示造成这次垃圾回收原因
类似的选项还有-gcutil 只是-gcutil没有LGCC和GCC而已。
也可以分开查看新生代和老年代垃圾回收和内存分配数据。分别是 -gcnew, -gcnewcapacity,-gcold,-gcoldcapacity,gcpermcapacity选项。
最后提一下printcompilation选项,用于打印JDK编译的方法。
9.jstatd
jstatd
同样用于监控JVM实例,不同于jstat的是它通过RMI方式,需要在被监控的java.home的bin下添加一个策略文件jstatd.all.policy:
grant codebase "file:${java.home}/../lib/tools.jar" {
permission java.security.AllPermission;
};
然后输入:
jstatd -J-Djava.security.policy=jstatd.all.policy -J-Djava.rmi.server.hostname=10.11.10.35 -p 1099
就启动了jstatd,下面就可以通过jvisualvm通过RMI的方式监控这台机器上的全部JVM实例。
其他详细情况可以参考
文档
10.jvisualvm
jvisualvm
是JDK提供的图形化jstat工具。可以监控本地的JVM实例,也可以通过JMX或RMI的方式监控远程的JVM实例。在Windows机器上,在cmd输入jvisualvm就可以启动:
11.jinfo
jinfo
用于打印特定JVM实例的配置信息。如果是运行在64Wie机器上,需要加一个-J-d64选项。基本语法如下:
jinfo [ option ] pid
比如我想查看29869这个进程运行的基本配置信息,可以输入:
jinfo 29869
得到的信息如下:
如果仅仅想查看JVM的系统变量,可以输入:
jinfo -sysprops 29869
12.jmap
jmap
用于查看JVM的so对象内存占用情况或指定的JVM实例堆内存情况。如果是JVM是运行在64位机器上,则需要添加-J-d64选项。
jmap -J-d64 29869
得到的信息如下:
如果输入:
jmap -J-d64 -heap 29869
得到的信息如下:
如果我们想查看这个JVM实例堆里对象的详细情况可以输入
jmap -J-d64 -histo:live 29869
加入
live
参数是只看到目前堆里存活的对象情况。
jmap -J-d64 -finalizerinfo 29869
查看等待回收的对象
最常用的是我们会生成一个dump文件使用下面的工具来查看,比如生成29869这个进程的dump文件可以这样:
jmap -J-d64 -dump:format=b,file=29869.bin 29869
13.jhat
jhat
和
jmap
配合使用,通过jmap生成的dump文件可以通过jhat解析浏览。基本语法如下:
jhat [ options ] <heap-dump-file>
比如,上面我们生成的29869.bin的dump文件,我们可以拷贝回Window机器上,然后输入
jhat 29869.bin
过一段比较长的解析时间(这个dump文件非常大,681M),就可以通过浏览器浏览这个dump文件的信息了。
可以通过加入一些选项来解析特定的信息,比如
-stack true/false
可以打开关闭堆栈的dump信息,默认是true。
-refs false/true
可以打开关闭对象的引用信息,
-port 7000
指定浏览器打开的端口,默认是7000
14.jstack
jstack
用于打印指定进程的调用堆栈信息,基本语法如下:
jstack [ option ] pid
如果是在64位机器上运行,可以加一个
-J-d64
参数。
jstack -J-d64 29869
得到的信息如下(部分截图):
-l
参数,可以将锁的详细信息打印出来,
-m
参数,可以用于混合模式(Java/C/C++)
上面总结的这些工具有的是基于Window下的用法,对应的Linux版本用法大同小异,详细差别以及使用可以见参考文献中对应的linux链接。大部分监控工具和调试工具使不分使用平台的。