性能测试: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

  1. 在本机打开终端,输入 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线程监控的主要内容,如果未能解决你的问题,请参考以下文章

性能测试:Java线程监控

性能测试之一个简单 Java 线程 dump 分析示例

性能测试(测试指标监控策略汇总)

性能测试之JVM监控

性能测试监控策略汇总

V共享测者的性能测试手册:JVM的监控利器