[experiment]
package com.infuq.tmp; public class Main { public static void main(String args[]) { for (;;) { try { Thread.sleep(5000); } catch (InterruptedException e) { e.printStackTrace(); } } } }
In the above code, let the JVM not exit. Let's do something about it and take a look at the two threads in the JVM
Run it after compiling
View process number through jps = 6617
View threads of process 6617
ps -Lf 6617
A total of 20 lightweight processes (LWP), i.e. threads
You can also check the number of tasks (i.e. threads) under process 6617 through / proc/6617/task, which is also 20 threads, as shown below
Let's take another look at the file descriptor opened by the process 6617, as follows
ls -l /proc/6617/fd
There are 6 file descriptors in total. 0, 1 and 2 are standard input, standard output and standard error output respectively. 3, 4 and 5 descriptors represent the three open jar s
To sum up, there are 20 threads in the JVM at this time, and the process has opened 6 file descriptors
Interview question: how to know the number of threads in the JVM and what are the methods?
Next, we create an. Attach in the / tmp directory_ Pid6617 file, as follows
Next, we use the kill command to send an exit signal to the process
Signaling mechanism is a way of inter process communication
Then observe the thread information
There is an extra thread of 6666
Then look at the file descriptor opened by process 6617
You will find an additional file description 6, which is also a socket file descriptor
To sum up, use the kill command to send an exit signal to the JVM process. As a result, the JVM has one more thread and one more sokcet file descriptor
There are many ways of inter process communication, among which signal is one of them Communication between processes can read it After sending a signal to the JVM, the JVM must have a thread to process the signal, and this thread is the Signal Dispatcher thread. I believe readers can see this thread when viewing the thread stack through the jstack command
The Signal Dispatcher thread is created when the JVM starts. Let's briefly talk about the startup of the JVM
In the jdk/src/share/bin/main.c file, there is a main method, which is the source of everything. The JVM starts its life journey from here. After a trot, it will create both a main thread and a JVM. It will also create a Signal Dispatcher thread, which will block waiting to receive external signals. For example, as mentioned above, The No. 3 exit signal sent by kill to the specified process 6617 is processed by the Signal Dispatcher thread in the process 6617. When the Signal Dispatcher thread receives and processes the No. 3 exit signal, it will create an Attach Listener thread and a socket file descriptor. The socket file descriptor is the No. 6 file descriptor seen above, So what does this socket file descriptor do?
In addition to signals, Unix Domain Socket can also be used for interprocess communication. This socket is different from network socket. Unix Domain Socket is only used for local interprocess communication, while network socket is used for interprocess communication between networks. The No. 6 file descriptor created through Unix Domain Socket, It is used by the Attach Listener thread. The Attach Listener thread acts as a server and listens to client requests. For example, tools such as jstack command and Ali's Arthas are connected to the target JVM through the socket file descriptor to realize communication
Through the tool jvisualvm in the bin directory of JDK, we can check the threads in process 6617 again in a graphical way
See if your company's server has these two threads?
Next, we get the thread stack information of process 6617 in three ways
Interview question: how to get the thread stack information of a process?
The first way is through jstack command or other commands of JDK system
The second way is through Java code
import com.sun.tools.attach.VirtualMachine; import sun.tools.attach.HotSpotVirtualMachine; import java.io.InputStream; public class Attach { public static void main(String[] args)throws Exception { // The bottom layer of attach sends a kill -3 6617 command to the target JVM VirtualMachine virtualMachine = VirtualMachine.attach("6617"); HotSpotVirtualMachine hotSpotVirtualMachine = (HotSpotVirtualMachine)virtualMachine; // Send the threaddump command to the target JVM InputStream inputStream = hotSpotVirtualMachine.remoteDataDump(new String[]{}); byte[] buff = new byte[256]; int len; do { // Receive the data returned by the target JVM len = inputStream.read(buff); if (len > 0) { String respone = new String(buff, 0, len, "UTF-8"); System.out.print(respone); } } while(len > 0); inputStream.close(); virtualMachine.detach(); } }
Compile and run the Java program, you can still get the thread stack information of process 6617
The third way is through the C language. The reason why we use the C language is to illustrate that whether we use the jstack command, the above Java program, or Ali's open source Arthas tool, at their bottom, we communicate with the target JVM in the same way. Through the C language, we can better show it to us
Personally, if you really want to learn the JVM or JDK thoroughly, you should be familiar with the C language. The bottom layer of the JVM is the C language, including some interactions with the operating system. Including inter process communication. If you don't understand the C language and some operating system knowledge, it's difficult to learn the JVM or JDK thoroughly. The reason why you want to learn the bottom knowledge such as the JVM is personal understanding, The main thing is to make our knowledge system sound and avoid knowledge fragmentation
The code is as follows
// threaddump.c #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/stat.h> #include <sys/socket.h> #include <sys/types.h> #include <sys/un.h> #include <errno.h> #include <stddef.h> #include <unistd.h> #define BUFFER_SIZE 8192 const char *filename = "/tmp/.java_pid6617"; int main(int argc, char **argv) { struct sockaddr_un un; int fd; char buffer[BUFFER_SIZE]; char *cmd = "1\0threaddump\0\0\0\0"; // Length 16 un.sun_family = AF_UNIX; strcpy(un.sun_path, filename); fd = socket(PF_UNIX, SOCK_STREAM, 0); connect(fd, (struct sockaddr *) &un, sizeof(un)); // Mode 1 send(fd, cmd, 16, 0); recv(fd, buffer, BUFFER_SIZE, 0); // Mode II //write(fd, cmd, 16); //read(fd, buffer, BUFFER_SIZE); printf("\n%s\n", buffer); close(fd); return 0; }
compile
function
As we can see above, the thread stack information is printed normally. So how does it do it?
First, a const char *filename = "/ tmp/.java_pid6617" is defined in the code; File name, let's look at this file
6617 is the process ID. when we send the No. 3 exit signal to the JVM through the kill Command, the Signal Dispatcher thread will create the Attach Listener thread, and the Attach Listener thread will create a / TMP /. Java based on the process ID_ PID < PID > file. If it is network socket communication, it is based on IP and port. If it is Unix Domain Socket communication, it is based on file. At this time, a / TMP /. Java is created_ PID < PID >, the Attach Listener thread will create a socket on the server side, and the client can use this / TMP /. Java_ PID < PID > file creates a client, and then communicates with the server. So how to create the socket of the client?
In our C language code
// Create a Unix Domain Socket for native interprocess communication fd = socket(PF_UNIX, SOCK_STREAM, 0); // Connect to the server. The server is also created through Unix Domain Socket connect(fd, (struct sockaddr *) &un, sizeof(un));
Through the above two sentences, the socket of the client is created, a connection is established with the server (that is, the target JVM), and then the command is sent
In the code, we send a threaddump command, as follows
char *cmd = "1\0threaddump\0\0\0\0"; // Length 16
Everything depends on the protocol. The client and the server have agreed on what kind of command format the server receives to indicate that the dump thread stack is required. Therefore, the client constructs such a command and sends it to the target JVM. After receiving the command, the Attach Listener thread of the target JVM processes it, and then returns the processing result to the client, So the client gets the thread stack of the target JVM
This article is so verbose, mainly expressing how to communicate with the target JVM, as well as some threads and knowledge points involved
official account