性能测试:Java线程监控
Posted 七月的小尾巴
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了性能测试:Java线程监控相关的知识,希望对你有一定的参考价值。
Java线程的五种状态:
- 新建:new
- 运行:runable
- 等待:waitting(无限期等待),timed waitting(期限等待)
- 阻塞:blocked
- 结束:terminated
Java线程监控 - Jvisualvm
图形界面工具,监控之前先对 jvm 加监控参数,在tomcat的bin目录下,catalina.sh 文件中,第二行添加:
JAVA_OPTS="-Djava.rmi.server.hostname=114.55.34.236 -Dcom.sun.management.jmxremote.port=9315 -Dcom.sun.management.jmxremote.ssl=false -Dcom.sun.management.jmxremote.authenticate=false"
注意hostname需要改成本机ip
配置成功之后,重启tomcat
- 在本机打开终端,输入 jvisualvm,打开 Java ViasualVM
输入远程主机的ip,点击确定
添加JMX链接
JMX连接输入ip+我们前面tomcat中配置的端口,因为配置中我们设置的是不需要SSL,和安全凭证,因此我们直接点击确认,就可以了。
连接成功之后,我们可以看到tomcat中java相关的进程。
jstack 工具
上面的组件,虽然很方便,但是他通常只支持测试环境查看数据,但是如果你想要看线上的数据,jvisualvm就无法满足我们的要求了。因为我们本地电脑通过都不支持连接到生产环境上的,通常都需要调板机之类的才能进行连接。
但是线程监控又是我们性能测试非常重要的一部分,需要密切关注,线程快我们整体性能就好。那么我们怎么去进行线上监控呢?
这里推荐使用jstack命令。
jstack <pid> // 后面设置我们的想要查看的进程ID
下方这个就是我们监控项目的进程数据。
注意这个命令需要我们服务器中安装了JDK环境,这个是JDK中的一个命令。
有些朋友可能一开始看到以为是报错了,但是其实不是的,这个就是我们的进程数据,每一段数据则表示一个进程,大家可以在Jvisualvm工具中,看到其实我们Jvisualvm的进程和服务器中的进程是一样的,只不过Jvisualvm是以图形化的界面来展示出来的,如图。
下面我们来介绍一下,具体怎么去查看这些数据呢?他们分别代表什么含义呢?
下面我复制了两个线程的数据,通常我们都叫做堆栈打印信息。
"RMI TCP Connection(944)-218.72.29.105" #90 daemon prio=5 os_prio=0 tid=0x00007f877cbee800 nid=0x1c23 runnable [0x00007f876ee48000]
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 <0x00000000fa622050> (a java.io.BufferedInputStream)
at java.io.FilterInputStream.read(FilterInputStream.java:83)
at sun.rmi.transport.tcp.TCPTransport.handleMessages(TCPTransport.java:555)
at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run0(TCPTransport.java:834)
at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.lambda$run$0(TCPTransport.java:688)
at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler$$Lambda$530/699994803.run(Unknown Source)
at java.security.AccessController.doPrivileged(Native Method)
at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run(TCPTransport.java:687)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
at java.lang.Thread.run(Thread.java:748)
"JMX server connection timeout 87" #87 daemon prio=5 os_prio=0 tid=0x00007f87782f2800 nid=0x762f in Object.wait() [0x00007f876e946000]
java.lang.Thread.State: TIMED_WAITING (on object monitor)
at java.lang.Object.wait(Native Method)
at com.sun.jmx.remote.internal.ServerCommunicatorAdmin$Timeout.run(ServerCommunicatorAdmin.java:168)
- locked <0x00000000c762be48> (a [I)
at java.lang.Thread.run(Thread.java:748)
java的堆栈信息分析
"RMI TCP Connection(944)-218.72.29.105"
首先上方的数据为线程的名称
prio=5
线程后方,我们可以看到有一个prio=5,这个prio指的是优先级。默认情况下,prio=5,数字越大,则表示优先级越高。
当我们cpu核数,每一个核同时只能做一件事情,如我们cup是8核,那么他只能同时处理8个任务。我们的cpu是不断的切换进程的,那么在切换的过程中,会根据优先级来,如果说你的线程优先级高,那么执行该线程的优先级就会高一些。
tid=0x00007f877cbee800
tid ,tid指的是jvm的线程ID,jvm内部线程的唯一标识。
咱们java程序,是运行在jvm环境中的,并不是直接在操作系统运行的。通常我们所说的线程ID,会有两个维度,一个是我们JVM内部的一个ID,这里我们叫tid。
nid=0x1c23
那么JVM的线程他是怎么执行的呢?他实际上是去调用我们操作系统的线程。他的底层是依赖于我们操作系统的线程,所以说每一个内部的线程ID,都会有一个操作系统的ID,是一一对应的。
nid:对应系统线程ID,和top命令查看的线程pid对应,不过一个是10进制,一个是16进制,可以通过 top -H -p pid
命令,来查看改进程的所有线程信息。
java.lang.Thread.State: RUNNABLE
上方这个是线程状态,当前这个状态是除于RUNNABLE状态,状态主要分为5中,如下:
- 新建:new
- 运行:runable
- 等待:waitting(无限期等待),timed waitting(期限等待)
- 阻塞:blocked
- 结束:terminated
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 <0x00000000fa622050> (a java.io.BufferedInputStream)
at java.io.FilterInputStream.read(FilterInputStream.java:83)
at sun.rmi.transport.tcp.TCPTransport.handleMessages(TCPTransport.java:555)
at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run0(TCPTransport.java:834)
at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.lambda$run$0(TCPTransport.java:688)
at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler$$Lambda$530/699994803.run(Unknown Source)
at java.security.AccessController.doPrivileged(Native Method)
at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run(TCPTransport.java:687)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
at java.lang.Thread.run(Thread.java:748)
上面这些信息,是我们当前线程所需要执行的函数,他的执行链路是从下往上执行的。如果想要看当前执行的函数,第一个就是。
有些细心朋友在查看数据的时候,可能会发现你的tomcat在监控数据的时候,会有很多waiting的线程,也许有些人会有疑惑,这么多等待线程,是不是就意味这我的程序性能上有问题呢?
其实不是的。看到waiting不一定是有问题的,有些其实是合理的。
比如说在我们没有做压测的情况下,tomcat会起很多线程,这些线程他们都是出于waiting阶段,这个就好比我们出租车,在没有派单给他们的时候,他们是处理等待状态,这个是正常的。waiting是否存在性能问题,这个需要结合实际场景。
以上是关于性能测试:Java线程监控的主要内容,如果未能解决你的问题,请参考以下文章