The author | He Bo
Arthas official Community is holding an essay call for prizes. Click to submit.
1. Introduction
background
Excessive CPU usage often occurs in online codes. According to my previous experience, I will use the TOP instruction and further check the specific information with the help of JSTACK to troubleshoot problems. However, basically, I cannot escape the situation of reissuing packages, even if it is an incremental package, and the application also needs to stop for a short time. Then the operations brothers asked me to give Arthas a try and said it would do hot updates to the code, so I came to give it a try.
For installation and basic use of Arthas, please refer to the following two articles:
- Arthas installs and listens to SpringBoot applications
- Arthas basic instructions to use
The environment
JDK1.8 SPringBoot 2.2.2 Arthas Linux
Test code:
@RequestMapping(value = "/bigThread")
@ResponseBody
public String bigThread(int id) {
ArthasService.test();
while (true) {
Thread t2 = new Thread();
t2.start();
id ++;
if(100000 == id) {
returnString.valueOf(id); }}}Copy the code
Train of thought
2. Thread-b checks whether a blocked thread exists
Thread-b: identifies the thread that is currently blocking other threads. If the thread is not found after execution, it indicates that the thread is not always blocking and executing.
3. Thread View the thread with the highest usage
If a thread is not followed by a parameter, the current information about all threads is displayed. I think thread-n 10 is sufficient.
As can be seen from the following figure, our application occupies 77% of the CPU in an instant. (Here, I check the status of thread at the moment of initiating the request, so it is more intuitive. The production environment should be more intuitive only when it is blocked and deadlocked.)
4. Thread ID View detailed information
Building on the previous step, let’s take a closer look at Thread 15 (because ID=15 above).
The thread is waiting for a condition to continue executing. As you can see, the method is called linkedBlockingQueue.take.
public E take(a) throws InterruptedException {
E x;
int c = -1;
final AtomicInteger count = this.count;
final ReentrantLock takeLock = this.takeLock;
takeLock.lockInterruptibly();
try {
while (count.get() == 0) {
notEmpty.await();
}
x = dequeue();
c = count.getAndDecrement();
if (c > 1)
notEmpty.signal();
} finally {
takeLock.unlock();
}
if (c == capacity)
signalNotFull();
return x;
}
Copy the code
AtomicInteger refers to atomicity under high concurrency, and ReentrantLock refers to ReentrantLock.
Notempty.await () consumes data from the queue. When the queue is empty, the thread blocks, so we know roughly that the problem is thread blocking, but we still don’t know which line of code is causing the problem.
If you know exactly what code has been changed this time, you can directly perform Step 6. If you don’t know, you can use Step 5 to locate the problem.
5. Watch checks which Controller executes the code
watch org.springframework.web.servlet.DispatcherServlet getHandler returnObj
Copy the code
This script detects all methods that are matched to the Handler via the DispatcherServlet, i.e. requests to the Controller, as follows:
After finding the corresponding code, we will further observe the abnormal information. There may be a question: why do I need to operate so tedious when I can check the error information through the log? My business scenario is: the log is still very large, just got it was brushed over, at this time the log location is not very good operation, of course, want to get down the log certainly can, also very intuitive, I generally go to view the log problem location, here is also to provide an idea.
6. Watch Indicates the exception information of the method
Watch class full path method name"{params[0],throwExp}" -e -x 2
Copy the code
As shown above, the error is presented intuitively, and it can be fixed below. Here, we can also check the execution time through the trace command:
The full path method name of the trace class"{params[0],throwExp}" -e -x 2
Copy the code
The following information is returned, as well as error messages and the duration of each method execution.
[arthas@10999]$ trace com.arthas.controller.OrderController bigThread
Press Q or Ctrl+C to abort.
Affect(class count: 1,method count: 1) cost in 53 ms.listenerId: 10 ` -ts=2020- 08 -19 14:45:57; thread_name=http-nio-0.0. 0. 0-8080-exec-10; id=16; is_daemon=true; priority=5; TCCL=org.springframework.boot.web.embedded.tomcat.TomcatEmbeddedWebappClassLoader@1f1c7bf6 `---[1452.684406ms] com.arthas.controller.OrderController:bigThread() [throws Exception]
+---[0.168814ms] com.arthas.service.ArthasService:test() #20` -throw:java.lang.OutOfMemoryError #-2 [unable to create new native thread]
Copy the code
7. Jad decompiles hot updates
Once we know the problem above, we can locate the problem.
Command: jad class full path method name
[arthas@13190]$ jad com.arthas.controller.OrderController ClassLoader: +-org.springframework.boot.loader.LaunchedURLClassLoader@17f052a3 +-sun.misc.Launcher$AppClassLoader@3d4eac69 +-sun.misc.Launcher$ExtClassLoader@45f45fa1 Location: file:/opt/software/arthas/Arthas.jar! /BOOT-INF/classes! //* * Decompiled with CFR. * * Could not load the following classes: * com.arthas.service.ArthasService * org.springframework.stereotype.Controller * org.springframework.web.bind.annotation.RequestMapping * org.springframework.web.bind.annotation.ResponseBody */
package com.arthas.controller;
import com.arthas.service.ArthasService;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
@Controller
public class OrderController {
@RequestMapping(value={"/bigThread"})
@ResponseBody
public String bigThread(int id) {
ArthasService.test();
do {
Thread t2 = new Thread();
t2.start();
} while (100000! = ++id);return String.valueOf(id);
}
}
Affect(row-cnt:1) cost in 1513 ms.
Copy the code
The code is decompiled at this point, so we need to output it as a Java file in order to be able to change it.
Instructions: jad com. Arthas. Controller. OrderController > / TMP/OrderController. Java
That is, jad full-path method name > storage path/storage name
Then go to the TMP directory to modify the Java file using vi. After the modification is complete, check the corresponding classloader to prepare for compilation.
sc -d *OrderController | grep classLoaderHash
mc -c 17f052a3 /tmp/OrderController.java -d /tmp
Copy the code
But there is a compilation error here.
So if we compile the class file locally and upload it, it’s the same.
Call before compilation
[arthas@13190]$ trace com.arthas.controller.OrderController bigThread
Press Q or Ctrl+C to abort.
Affect(class count: 1,method count: 1) cost in 77 ms.listenerId: 2 ` -ts=2020- 08 -19 15:51:46; thread_name=http-nio-0.0. 0. 0-8080-exec-1; id=d; is_daemon=true; priority=5; TCCL=org.springframework.boot.web.embedded.tomcat.TomcatEmbeddedWebappClassLoader@1f1c7bf6 `---[6734.666529ms] com.arthas.controller.OrderController:bigThread() [throws Exception]
+---[0.786517ms] com.arthas.service.ArthasService:test() #20` -throw:java.lang.OutOfMemoryError #-2 [unable to create new native thread]
Copy the code
Pre-update code
@RequestMapping(value = "/bigThread")
@ResponseBody
public String bigThread(int id) {
ArthasService.test();
while (true) {
Thread t2 = new Thread();
t2.start();
id ++;
if(100000 == id) {
returnString.valueOf(id); }}}Copy the code
Updated code
@RequestMapping(value = "/bigThread")
@ResponseBody
public String bigThread(int id) {
ArthasService.test();
Thread t2 = new Thread();
t2.start();
return "success";
}
Copy the code
Compile the instruction
[arthas@13190]$ redefine /tmp/OrderController.class
redefine success, size: 1, classes:
com.arthas.controller.OrderController
Copy the code
Called three times after compilation
`---ts=2020- 08 -19 15:52:02; thread_name=http-nio-0.0. 0. 0-8080-exec-3; id=f; is_daemon=true; priority=5; TCCL=org.springframework.boot.web.embedded.tomcat.TomcatEmbeddedWebappClassLoader@1f1c7bf6 `---[5.609405ms] com.arthas.controller.OrderController:bigThread()
`---[0.204675ms] com.arthas.service.ArthasService:test() #20
`---ts=2020- 08 -19 15:52:04; thread_name=http-nio-0.0. 0. 0-8080-exec-4; id=10; is_daemon=true; priority=5; TCCL=org.springframework.boot.web.embedded.tomcat.TomcatEmbeddedWebappClassLoader@1f1c7bf6 `---[3.900149ms] com.arthas.controller.OrderController:bigThread()
`---[0.14636ms] com.arthas.service.ArthasService:test() #20
`---ts=2020- 08 -19 15:52:04; thread_name=http-nio-0.0. 0. 0-8080-exec-5; id=11; is_daemon=true; priority=5; TCCL=org.springframework.boot.web.embedded.tomcat.TomcatEmbeddedWebappClassLoader@1f1c7bf6 `---[1.90945ms] com.arthas.controller.OrderController:bigThread()
`---[0.147353ms] com.arthas.service.ArthasService:test() #20
Copy the code
It can be found that the time changes from 6734.666529ms to about 3ms, indicating that the hot update code has taken effect.
Make a flame profile for subsequent analysis
As shown below:
Arthas’s essay campaign is in full swing
Arthas is officially holding an essay call if you have:
- Problems identified using Arthas
- Source code interpretation of Arthas
- Advise Arthas
- No limit, other Arthas related content
Welcome to participate in the essay activity, there are prizes to win oh ~ click to submit
“Alibaba Cloud originator focuses on micro-service, Serverless, container, Service Mesh and other technical fields, focuses on the trend of cloud native popular technology, large-scale implementation of cloud native practice, and becomes the public account that most understands cloud native developers.”