Java监控工具
jcmd : jvm 性能调优工具之 jcmd
示例:
#显示本地存在的java进程
jcmd
#显示pid为1242进程可以执行的相关命令
jcmd 1242 help
#显示pid为1242进程的执行时长
jcmd 1242 VM.uptime
jconsole:图形化监控工具
示例:
在命令行执行jconsole命令,显示可以监控的java本地进程列表,也可以选择远程进程进行连接:
选择感兴趣的进程点击Connect,显示关于该进程的一些统计信息(内存、线程、CPU等信息),包含变化曲线趋势图(利于分析性能问题):
jmap:java内存转储工具,可以理解为将jvm的整体内存使用信息dump到磁盘上进行事后分析
使用如下命令将pid为342的java进程转储到本地文件,文件名为dump,该文件可以用分析工具打开,比较有名的如MemoryAnalyzer(eclipse的开源软件,非常好用),打开之后便可以看到内存中的详细信息。曾经本人定位一个非常严重的内存泄露问题(大数据组件spark 1.5以下版本的某个bug),便是通过这个工具定位出来的。
% jmap -dump:file=dump 342 ~/IdeaProjects/learnGradle
Dumping heap to /Users/zbao/IdeaProjects/learnGradle/dump ...
Heap dump file created
jstack:转储进程的栈信息。可以打印出进程运行的瞬时状态,主要是各线程的运行状态。曾经本人定位线程的死锁问题便是通过此工具定位的。
如下命令,jstack 进程pid即可:
jstack 342
打印出进程里线程的运行状态:
% jstack 342 ~/IdeaProjects/learnGradle
2018-09-10 21:40:46
Full thread dump OpenJDK 64-Bit Server VM (25.152-b39 mixed mode):
"Attach Listener" #122 daemon prio=9 os_prio=31 tid=0x00007fe16a732000 nid=0x1ea3b waiting on condition [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"ApplicationImpl pooled thread 30" #114 prio=4 os_prio=31 tid=0x00007fe16d499800 nid=0x1617f waiting on condition [0x000070000e68c000]
java.lang.Thread.State: TIMED_WAITING (parking)
at sun.misc.Unsafe.park(Native Method)
- parking to wait for <0x00000007a1fd6320> (a java.util.concurrent.SynchronousQueue$TransferStack)
at java.util.concurrent.locks.LockSupport.parkNanos(LockSupport.java:215)
at java.util.concurrent.SynchronousQueue$TransferStack.awaitFulfill(SynchronousQueue.java:460)
at java.util.concurrent.SynchronousQueue$TransferStack.transfer(SynchronousQueue.java:362)
at java.util.concurrent.SynchronousQueue.poll(SynchronousQueue.java:941)
at java.util.concurrent.ThreadPoolExecutor.getTask(ThreadPoolExecutor.java:1066)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1127)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
at java.lang.Thread.run(Thread.java:745)
"ApplicationImpl pooled thread 29" #113 daemon prio=4 os_prio=31 tid=0x00007fe16b69a800 nid=0x1030f waiting on condition [0x000070000dae8000]
java.lang.Thread.State: TIMED_WAITING (parking)
at sun.misc.Unsafe.park(Native Method)
- parking to wait for <0x00000007a1fd6320> (a java.util.concurrent.SynchronousQueue$TransferStack)
at java.util.concurrent.locks.LockSupport.parkNanos(LockSupport.java:215)
at java.util.concurrent.SynchronousQueue$TransferStack.awaitFulfill(SynchronousQueue.java:460)
at java.util.concurrent.SynchronousQueue$TransferStack.transfer(SynchronousQueue.java:362)
at java.util.concurrent.SynchronousQueue.poll(SynchronousQueue.java:941)
at java.util.concurrent.ThreadPoolExecutor.getTask(ThreadPoolExecutor.java:1066)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1127)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
at java.lang.Thread.run(Thread.java:745)
"ApplicationImpl pooled thread 24" #108 daemon prio=4 os_prio=31 tid=0x00007fe16e3cc000 nid=0xce43 waiting on condition [0x000070000b14b000]
java.lang.Thread.State: TIMED_WAITING (parking)
at sun.misc.Unsafe.park(Native Method)
- parking to wait for <0x00000007a1fd6320> (a java.util.concurrent.SynchronousQueue$TransferStack)
at java.util.concurrent.locks.LockSupport.parkNanos(LockSupport.java:215)
at java.util.concurrent.SynchronousQueue$TransferStack.awaitFulfill(SynchronousQueue.java:460)
at java.util.concurrent.SynchronousQueue$TransferStack.transfer(SynchronousQueue.java:362)
at java.util.concurrent.SynchronousQueue.poll(SynchronousQueue.java:941)
at java.util.concurrent.ThreadPoolExecutor.getTask(ThreadPoolExecutor.java:1066)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1127)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
at java.lang.Thread.run(Thread.java:745)
"RMI TCP Connection(114)-127.0.0.1" #71 daemon prio=4 os_prio=31 tid=0x00007fe16dafd000 nid=0x10003 runnable [0x000070001015f000]
java.lang.Thread.State: RUNNABLE
at java.net.SocketInputStream.socketRead0(Native Method)
at java.net.SocketInputStream.socketRead(SocketInputStream.java:116)
at java.net.SocketInputStream.read(SocketInputStream.java:171)
at java.net.SocketInputStream.read(SocketInputStream.java:141)
at java.io.BufferedInputStream.fill(BufferedInputStream.java:246)
at java.io.BufferedInputStream.read(BufferedInputStream.java:265)
- locked <0x00000007965728c0> (a java.io.BufferedInputStream)
at java.io.FilterInputStream.read(FilterInputStream.java:83)
at sun.rmi.transport.tcp.TCPTransport.handleMessages(TCPTransport.java:550)
at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run0(TCPTransport.java:826)
at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.lambda$run$0(TCPTransport.java:683)
at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler$$Lambda$1328/54941826.run(Unknown Source)
at java.security.AccessController.doPrivileged(Native Method)
at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run(TCPTransport.java:682)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
at java.lang.Thread.run(Thread.java:745)
jvisualvm:一款jvm自带的本地监控工具。功能简单但是够用,可以监控多项指标和功能,包括内存转储的实时结果显示。
其中,功能比较强大的还有Oracle商业版的JFR,飞行记录器,这个工具由于是收费的,就不介绍了,有兴趣的可以自行google,该工具有很多高级功能。
Java内存模型
Java与传统语言如C、C++等最大的不同在于内存管理机制,Java无需自己管理内存,交给相应的垃圾收集器进行内存管理。jvm中的线程从垃圾收集的角度可以分成两类:应用线程和垃圾收集线程。垃圾收集线程在需要进程垃圾收集时,找出不再使用的内存释放掉,同时,为了预防出现大量内存碎片导致无法继续分配对象(想象一下本科学习操作系统时分配内存导致的内存碎片,是同一个道理),可能还需要对内存进行整理和压缩(跟操作系统中的内存整理类似)。
可以想象,垃圾收集器是把双刃剑,因为在垃圾收集时,对象地址可能会发生变化,可能需要停止应用线程的工作,full gc所导致的进程故障在实际场景中比较常见(本人曾经定位过非常严重的问题中包含full gc问题);同时,垃圾收集线程也需要占用系统资源;最后,这种内存管理方式没有办法精确的知道内存的使用状况。但是带来的好处也显而易见,不用再小心翼翼进行内存申请和释放。
调优垃圾收集器时,最重要的考量因素就是gc所导致的时空停顿(stop the world)。
所有的垃圾收集器都是分代垃圾收集器:所有GC算法都将jvm的内存空间分成新生代和老年代。
Java现有的收集器有以下几种:Serial收集器,Throughput(Parallel)收集器、Concurrent收集器(CMS)和G1收集器。
该文章待持续补充。