目录
•写在前面
•jps虚拟机进程状况工具
•jstat虚拟机统计信息监视工具
•jinfo配置信息工具
•jmap内存映像工具
•jhat虚拟机堆转储快照分析工具
•jstack堆栈跟踪器
•JConsole 监视与管理控制台
•VisualVM多合一故障处理工具
我们平时使用java时,基本都是将下载下来的JDK包用来当基础环境用,一般程序运行包括测试监控都是依靠编译器来完成操作的,毕竟使用编译器集成的各种工具,对于我们日常的各种开发所需的工具其实都绰绰有余,不过,今天我还是想要讲一讲JDK给我附带的许多容易被我们忽略的命令行小工具,这些工具对于初学者可能使用不到,但当我们涉及需要对java程序警醒性能监控以及定位故障的时候,这些小工具发挥着巨大的作用。
如果之前没有接触过这些小工具,可以打开JDK安装目录下的bin目录里面,你会发现一块新天地,里面有着相当多的exe程序,这些程序非常的小巧。如果有兴趣了解这些小工具是怎么实现的(这已经涉及到底层源码级的东西了),可以去JDK安装目录下的lib目录的tools.jar看看,因为这些小工具之所以小巧,大多数都是基于这个jar包进行一层很薄的封装而已(值得注意的是,如果是Linux系统,这些工具是由Shell脚本写的)。
说到这里可能敏感的人已经意识到了什么,既然这些小工具是基于tools.jar实现的,那我们是不是可以在自己的程序代码里调用这个jar包,实现监控分析功能?这完全是可以的,而且对于那些经常和底层打交道的人来说,这也是很普遍的一件事儿,很多时候我们在部署程序到生产环境的时候,如论是直接接触物理服务器还是Telnet到服务器,我们都可以接触tools.jar类库里面的接口,直接在应用程序中实现强大的监控分析功能。这篇文章讲一些使用较为普遍的工具,至于其他的工具,可以直接在官方的说明(多废话一下,运行的时候要通过cmd到bin目录下执行哦,别直接双击程序,双击给我喊六六六就好了,给我点赞,哈哈哈)。
这个小工具可以列出正在运行的虚拟机进程,并显示虚拟机执行主类(就是我们的main()函数所在的类)名称以及这些进程的本地虚拟机唯一ID。别看这个东西使用功能单一,但是这个算是经常使用到的JDK命令行工具,因为其他的JDK工具大多需要输入它查询到的LVMID来确定要监控的是哪一个虚拟机进程。对于本地虚拟机进程来说,LVMID于操作系统的进程ID是一致的,我们可以使用Windows的任务管理器查询到这个ID,但是吐过多个虚拟机进程,无法根据进程名称定位时,那就只能依赖jps命令显示主类的功能进行区分。
(请忽略中间一段手残打错字,小声哔哔)看我执行了一个小命令,直接输出了我此时虚拟机主类的全名。这里我列出它常用的一些命令:
jps -q #只输出LVMID,省略主类的名称
jps -m #输出虚拟机进程启动时传递给主类main()函数的参数
jps -l #输出主类的全名,如果进程执行的是Jar包,输出jar路径
jps -v #输出虚拟机进程启动时JVM参数
jstat是用于监视虚拟机各种运行状态的信息的命令行工具,它可以显示本地或者远程虚拟机进程中的类加载、内存、垃圾收集、JIT编译等运行数据,在没有GUI图形界面,只提供了纯文本控制台环境的服务器上,它将是运行期定位虚拟机性能问题的首选工具。
命令格式
jstat [option] LVMID [interval] [count]
参数
[option] : 操作参数
LVMID : 本地虚拟机进程ID
[interval] : 连续输出的时间间隔
[count] : 连续输出的次数
对于命令格式中的VMID与LVMID需要特别说明一下:如果是本地虚拟机进程,VMID与LVMID是一致的;如果是远程虚拟机进程,那VMID的格式应当是:protocol://lvmid@hostname:port/servername
举个例子,假设需要每250毫秒查询一次进程2764垃圾收集状况,一共查询20次,那命令应当是:jstat -gc 2764 250 20
jinfo的作用是实时的查看和调整虚拟机各项参数,使用jps命令的-v参数可以查看虚拟机启动时显示指定的虚拟机启动参数列表,但如果想知道未被显式指定的参数的系统默认值,除了找资料外,就只能通过jinfo的-flag进行查询了
命令格式
jinfo [option] [args] LVMID
option参数
-flag : 输出指定args参数的值
-flags : 不需要args参数,输出所有JVM参数的值
-sysprops : 输出系统属性,等同于System.getProperties()
jmap命令用于生成堆转储快照(一般称为heapdump或dump文件)。如果不使用jmap命令,要想获取Java堆转储快照,还有一些比较“暴力”的手段:譬如加-XX:+HeapDumpOnOutOfMemoryError参数,可以让虚拟机在OOM异常出现之后自动生成dump文件,通过-XX:+HeapDumpOnCtrlBreak参数则可以使用[Ctrl]+[Break]键让虚拟机生成dump文件,又或者在Linux系统下通过Kill-3命令发送进程退出信号“吓唬”一下虚拟机,也能拿到dump文件。jmap的作用并不仅仅是为了获取dump文件,它还可以查询finalize执行队列、Java堆和永久代的详细信息,如空间使用率、当前用的是哪种收集器等。和jinfo命令一样,jmap有不少功能在Windows平台下都是受限的,除了生成dump文件的-dump选项和用于查看每个类的实例、空间占用统计的-histo选项在所有操作系统都提供之外,其余选项都只能在Linux/Solaris下使用。
命令格式
jmap [option] LVMID
option参数
dump : 生成堆转储快照,格式为:-dump:[live, ] format=b,file=,其中live子参数说明是否只dump出存活的对象。
finalizerinfo : 显示在F-Queue队列等待Finalizer线程执行finalizer方法的对象
heap : 显示Java堆详细信息
histo : 显示堆中对象的统计信息,GC使用的算法,heap的配置及wise heap的使用情况,可以用此来判断内存目前的使用情况以及垃圾回收情况
permstat : to print permanent generation statistics
F : 当-dump没有响应时,强制生成dump快照
为了方便理解,我另贴解释出来
Attaching to process ID 28920, please wait...
Debugger attached successfully.
Server compiler detected.
JVM version is 24.71-b01
using thread-local object allocation.
Parallel GC with 4 thread(s)//GC 方式
Heap Configuration: //堆内存初始化配置
MinHeapFreeRatio = 0 //对应jvm启动参数-XX:MinHeapFreeRatio设置JVM堆最小空闲比率(default 40)
MaxHeapFreeRatio = 100 //对应jvm启动参数 -XX:MaxHeapFreeRatio设置JVM堆最大空闲比率(default 70)
MaxHeapSize = 2082471936 (1986.0MB) //对应jvm启动参数-XX:MaxHeapSize=设置JVM堆的最大大小
NewSize = 1310720 (1.25MB)//对应jvm启动参数-XX:NewSize=设置JVM堆的‘新生代’的默认大小
MaxNewSize = 17592186044415 MB//对应jvm启动参数-XX:MaxNewSize=设置JVM堆的‘新生代’的最大大小
OldSize = 5439488 (5.1875MB)//对应jvm启动参数-XX:OldSize=:设置JVM堆的‘老生代’的大小
NewRatio = 2 //对应jvm启动参数-XX:NewRatio=:‘新生代’和‘老生代’的大小比率
SurvivorRatio = 8 //对应jvm启动参数-XX:SurvivorRatio=设置年轻代中Eden区与Survivor区的大小比值
PermSize = 21757952 (20.75MB) //对应jvm启动参数-XX:PermSize=:设置JVM堆的‘永生代’的初始大小
MaxPermSize = 85983232 (82.0MB)//对应jvm启动参数-XX:MaxPermSize=:设置JVM堆的‘永生代’的最大大小
G1HeapRegionSize = 0 (0.0MB)
Heap Usage://堆内存使用情况
PS Young Generation
Eden Space://Eden区内存分布
capacity = 33030144 (31.5MB)//Eden区总容量
used = 1524040 (1.4534378051757812MB) //Eden区已使用
free = 31506104 (30.04656219482422MB) //Eden区剩余容量
4.614088270399305% used //Eden区使用比率
From Space: //其中一个Survivor区的内存分布
capacity = 5242880 (5.0MB)
used = 0 (0.0MB)
free = 5242880 (5.0MB)
0.0% used
To Space: //另一个Survivor区的内存分布
capacity = 5242880 (5.0MB)
used = 0 (0.0MB)
free = 5242880 (5.0MB)
0.0% used
PS Old Generation //当前的Old区内存分布
capacity = 86507520 (82.5MB)
used = 0 (0.0MB)
free = 86507520 (82.5MB)
0.0% used
PS Perm Generation//当前的 “永生代” 内存分布
capacity = 22020096 (21.0MB)
used = 2496528 (2.3808746337890625MB)
free = 19523568 (18.619125366210938MB)
11.337498256138392% used
670 interned Strings occupying 43720 bytes.
jhat一看就会想到和jmap搭配使用,一个生成快照,一个分析快照。值得一提的是,jhat内置了一个HTTP/HTML服务器,生成dump文件的分析结果之后,可以在浏览器中查看,不过一般来说,我们不用jhat(除非你手上真的没有别的工具可以使用了)。这是为什么呢?一是一般不会在部署应用程序的服务器上直接分析dump文件,即使可以这样做,也会尽量将dump文件复制到其他机器。二是用于分析的机器一般也是服务器,由于加载dump快照文件需要比生成dump更大的内存,所以一般在64位JDK、大内存的服务器上进行分析,因为分析工作是一个耗时而且消耗硬件资源的过程,既然都要在其他机器进行,就没有必要受到命令行工具的限制了;另一个原因是jhat的分析功能相对来说比较简陋,VisualVM,以及专业用于分析dump文件的Eclipse Memory Analyzer、IBM HeapAnalyzer等工具,都能实现比jhat更强大更专业的分析功能。
命令格式
jhat [option] [dumpfile]
参数
-stack false|true 关闭对象分配调用栈跟踪(tracking object allocation call stack)。 如果分配位置信息在堆转储中不可用. 则必须将此标志设置为 false. 默认值为 true.>
-refs false|true 关闭对象引用跟踪(tracking of references to objects)。 默认值为 true. 默认情况下, 返回的指针是指向其他特定对象的对象,如反向链接或输入引用(referrers or incoming references), 会统计/计算堆中的所有对象。>
-port port-number 设置 jhat HTTP server 的端口号. 默认值 7000.>
-exclude exclude-file 指定对象查询时需要排除的数据成员列表文件(a file that lists data members that should be excluded from the reachable objects query)。 例如, 如果文件列列出了 java.lang.String.value , 那么当从某个特定对象 Object o 计算可达的对象列表时, 引用路径涉及 java.lang.String.value 的都会被排除。>
-baseline exclude-file 指定一个基准堆转储(baseline heap dump)。 在两个 heap dumps 中有相同 object ID 的对象会被标记为不是新的(marked as not being new). 其他对象被标记为新的(new). 在比较两个不同的堆转储时很有用.>
-debug int 设置 debug 级别. 0 表示不输出调试信息。 值越大则表示输出更详细的 debug 信息.>
-version 启动后只显示版本信息就退出>
-J< flag > 因为 jhat 命令实际上会启动一个JVM来执行, 通过 -J 可以在启动JVM时传入一些启动参数. 例如, -J-Xmx512m 则指定运行 jhat 的Java虚拟机使用的最大堆内存为 512 MB. 如果需要使用多个JVM启动参数,则传入多个 -Jxxxxxx.
跟前面内存快照不同,jstack命令用于生成虚拟机当前时刻的线程快照(也就是成为threaddump或者Javacore文件)线程快照就是当前虚拟机内每一条线程正在执行的方法堆栈的集合,生成线程快照的主要目的是定位线程出现长时间停顿的原因,如线程间死锁、死循环、请求外部资源导致的长时间等待等都是导致线程长时间停顿的常见原因。线程出现停顿的时候通过jstack来查看个线程的调用堆栈,就可以知道没有响应的线程到底在后台做些什么事情,或者等待什么资源。
jstack [option] vmid
-F #当正常输出的请求不被响应时,强制输出线程堆栈
-l #除堆栈外,显示关于锁的附加信息
-m #如果调用到本地方法的话,可以显示C/C++的堆栈
当然我们可以直接Thread调用方法就可以实现jstack的大部分功能,就可以不用对着jstack的黑白发愣了,哈哈哈。
JConsole是一种基于JMX的可视化监视管理工具,它管理部分的功能是针对JMX MBean进行管理,由于MBean可以使用代码、中间件服务器的管理控制台或者所有符合JMX规范软件进行访问。老样子在bin目录下找到jconsole.exe,这次是直接从这里双击就可以了,大概长这样
启动后,将自动搜索出本机运行的所有虚拟机进程,不需要用户自己再使用jps查询了。比如我们选中本地进程,点击其中一个进程就可以开始进行监控了。监控的概述界面大概长这样,其内存呀、线程呀之类的自己点,我这里就不贴出来。
突然讲到这个贼强大的工具有点子激动,VisualVM是到目前为止随JDK发布的功能最强大的运行监视和故障处理程序,并且可以预见在未来一段时间内都是官方主力发展的虚拟故障处理工具。知道官方在这个软件说明上咋描述的不“All-in-One”,自己想想。VisualVM的还有一个很大的优点,不需要被监视的程序基于特殊Agent运行,因此它对应用程序的实际性能的影响很小,使得它可以直接应用在生产环境中。在以前JDK老版本中,没有这个,需要自己去下载插件,但是我在用JDK8,里面可以直接找到jvisualvm.exe,和JConsole一样,直接双击运行就可以了,大概长这样子。
使用visualVM还可以装其他的插件,这也是我比较喜欢用它的原因,反正各种工具都各有自己的优势,在各种环境中灵活挑选使用才是硬道理。