前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Arthas教你秒解GPU、内存、接口问题!提升排查效率轻松搞定!

Arthas教你秒解GPU、内存、接口问题!提升排查效率轻松搞定!

作者头像
码农飞哥
发布2024-01-22 17:26:44
1.5K0
发布2024-01-22 17:26:44
举报
文章被收录于专栏:好好学习好好学习

1. Arthas是什么

Arthas(阿尔萨斯)是一款由阿里巴巴开源团队开发的Java应用性能监控与诊断工具。它作为一种开源的Java诊断工具,主要用于在生产环境中实时监控、分析和诊断Java应用程序的性能问题。Arthas提供了一系列的命令行工具,可以实时查看Java应用的运行状态、堆栈信息、方法执行耗时等关键性能数据,帮助开发者快速定位并解决问题。

2. Arthas的使用场景

Arthas适用于各种Java应用场景,特别是在生产环境中解决实时性能问题。以下是一些常见的使用场景:

2.1 性能问题定位

Arthas可以实时监控应用程序的性能数据,包括方法执行时间、线程状态等,帮助开发者快速定位潜在的性能瓶颈。

2.2 实时调试

在生产环境中,使用Arthas可以实时进行代码调试,查看变量的值、修改变量的值,甚至动态加载类,而不需要重启应用。

2.3 内存分析

Arthas支持对Java应用的内存进行实时分析,帮助开发者查找内存泄漏、优化内存使用等问题。

2.4 线上问题快速定位

Arthas可以在生产环境中实时诊断问题,无需重启应用,从而快速定位线上故障,提高故障排查效率。

3. Arthas怎么使用

Arthas的使用相对简单,主要通过命令行工具进行操作。以下是一些基本的使用步骤:

3.1 下载安装

首先,通过官方网站或Maven仓库下载Arthas,并解压到本地目录。

官方文档:https://alibaba.github.io/arthas

到官方的开源地址:https://github.com/alibaba/arthas,或者国内的Gitee地址下去下载arthas的jar包。

代码语言:javascript
复制
# github下载
wget https://alibaba.github.io/arthas/arthas-boot.jar
# 或者 Gitee 下载
wget https://arthas.gitee.io/arthas-boot.jar
# 打印帮助信息
java -jar arthas-boot.jar -h

3.2 运行Arthas

Arthas 只是一个 java 程序,所以可以直接用 java -jar 运行。运行时或者运行之后要选择要监测的 Java 进程。

代码语言:javascript
复制
# 运行方式1,先运行,在选择 Java 进程 PID
java -jar arthas-boot.jar
# 选择进程(输入[]内编号(不是PID)回车)
[INFO] arthas-boot version: 3.5.0
[INFO] Found existing java process, please choose one and hit RETURN.
* [1]: 24480 com.Arthas
  [2]: 20520 com.jay.demos.ArthasTest
  [3]: 16200 org.jetbrains.jps.cmdline.Launcher
  [4]: 21032 org.jetbrains.idea.maven.server.RemoteMavenServer

# 运行方式2,运行时选择 Java 进程 PID
java -jar arthas-boot.jar [PID]

3.4 使用Arthas命令

一旦连接成功,可以使用各种Arthas命令进行实时监控、诊断,例如:dashboard查看仪表盘、trace追踪方法调用、watch监控变量等。下面列举一些常用命令。

命令

介绍

dashboard

当前系统的实时数据面板

thread

查看当前 JVM 的线程堆栈信息

watch

方法执行数据观测

trace

方法内部调用路径,并输出方法路径上的每个节点上耗时

stack

输出当前方法被调用的调用路径

tt

方法执行数据的时空隧道,记录下指定方法每次调用的入参和返回信息,并能对这些不同的时间下调用进行观测

monitor

方法执行监控

jvm

查看当前 JVM 信息

vmoption

查看,更新 JVM 诊断相关的参数

sc

查看 JVM 已加载的类信息

sm

查看已加载类的方法信息

jad

反编译指定已加载类的源码

classloader

查看 classloader 的继承树,urls,类加载信息

heapdump

类似 jmap 命令的 heap dump 功能

3.5 退出

使用 shutdown 退出时 Arthas 同时自动重置所有增强过的类 。

4. Arthas的常用操作

上面已经了解了Arthas的使用场景以及启动方式,下面就来说说Arthas的使用方式。

首先,编写一个有各种异常场景的代码。这个代码模拟了CPU过高,线程阻塞,线程死锁,内存不断被消耗等场景。

将代码上传到Linux服务器上,通过 1. javac ArthasTest.java 命令编译将ArthasTest.java文件编译成ArthasTest.class文件。接着通过 java ArthasTest 命令来运行此文件。

代码语言:javascript
复制
import java.util.HashSet;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * <p>
 * Arthas Demo
 * 公众号:码农飞哥
 *
 * @Author 码农飞哥
 */

public class ArthasTest {
	
	private static HashSet hashSet = new HashSet();
	/**
	 * 线程池,大小1
	 */
	private static ExecutorService executorService = Executors.newFixedThreadPool(1);

	public static void main(String[] args) {
		// 模拟 CPU 过高
		cpu();
		// 模拟线程阻塞
		thread();
		// 模拟线程死锁
		deadThread();
		// 不断的向 hashSet 集合增加数据
		addHashSetThread();
	}

	/**
	 * 不断的向 hashSet 集合添加数据
	 */
	public static void addHashSetThread() {
		// 初始化常量
		new Thread(() -> {
			int count = 0;
			while (true) {
				try {
					hashSet.add("count" + count);
					Thread.sleep(10000);
					count++;
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
		}).start();
	}

	public static void cpu() {
		cpuHigh();
		cpuNormal();
	}

	/**
	 * 极度消耗CPU的线程
	 */
	private static void cpuHigh() {
		Thread thread = new Thread(() -> {
			while (true) {
				System.out.println("极度消耗CPU的线程,任务死循环,cpu start 100");
			}
		});
		// 添加到线程
		executorService.submit(thread);
	}

	/**
	 * 普通消耗CPU的线程
	 */
	private static void cpuNormal() {
		for (int i = 0; i < 10; i++) {
			new Thread(() -> {
				while (true) {
					System.out.println("普通消耗CPU的线程,任务睡眠3000毫秒,死循环,cpu start");
					try {
						Thread.sleep(3000);
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}
			}).start();
		}
	}

	/**
	 * 模拟线程阻塞,向已经满了的线程池提交线程
	 */
	private static void thread() {
		Thread thread = new Thread(() -> {
			while (true) {
				System.out.println("模拟线程阻塞,thread start");
				try {
					Thread.sleep(3000);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
		});
		// 添加到线程
		executorService.submit(thread);
	}

	/**
	 * 死锁
	 */
	private static void deadThread() {
		/** 创建资源 */
		Object resourceA = new Object();
		Object resourceB = new Object();
		// 创建线程
		Thread threadA = new Thread(() -> {
			synchronized (resourceA) {
				System.out.println(Thread.currentThread() + " get ResourceA");
				try {
					Thread.sleep(1000);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				System.out.println(Thread.currentThread() + "waiting get resourceB");
				synchronized (resourceB) {
					System.out.println(Thread.currentThread() + " get resourceB");
				}
			}
		});

		Thread threadB = new Thread(() -> {
			synchronized (resourceB) {
				System.out.println(Thread.currentThread() + " get ResourceB");
				try {
					Thread.sleep(1000);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				System.out.println(Thread.currentThread() + "waiting get resourceA");
				synchronized (resourceA) {
					System.out.println(Thread.currentThread() + " get resourceA");
				}
			}
		});
		threadA.start();
		threadB.start();
	}
}

4.1. 全局监控

首先通过 dashboard 命令查看当前系统的实时数据面板。结果如下,我们看到有两个线程的状态是BLOCKED状态。

4.2. 定位CPU最高的方法

上面的代码例子有一个 CPU 空转的死循环,非常的消耗 CPU性能,那么怎么找出来呢?

使用 thread查看所有线程信息,同时会列出每个线程的 CPU 使用率,可以看到图里 ID 为12 的线程 CPU 使用100%。

使用命令 thread 12 查看 CPU 消耗较高的 12 号线程信息,可以看到 CPU 使用较高的方法和行数。

上面是通过先观察总体的线程信息,然后查看具体的线程运行情况,如果只是为了寻找CPU使用较高的线程,那么可以通过 thread -n[显示的线程个数],就可以排列出 CPU 使用率 Top N 的线程。

4.3. 线程死锁

在介绍线程死锁之前,首先回顾一下线程的几种常见状态:

  1. RUNNABLE : 线程正在运行状态
  2. TIMED_WAITIN:线程在等待运行中,调用以下方法会进入TIMED_WAITIN状态
    1. Thread#sleep()
    2. Object#wait() 并加了超时参数
    3. Thread#join() 并加了超时参数
    4. LockSupport#parkNanos()
    5. LockSupport#parkUntil()
  3. WAITIN:线程在等待运行中,调用以下方法会进入WAITIN状态
  4. Thread#sleep()
  5. Object#wait() 并加了超时参数
  6. Thread#join() 并加了超时参数
  7. LockSupport#parkNanos()
  8. LockSupport#parkUntil()
  9. BLOCKED 阻塞,等待锁

上面的模拟代码里,定义了线程池大小为1 的线程池,然后在 cpuHigh 方法里提交了一个线程,在 thread方法再次提交了一个线程,后面的这个线程因为线程池已满,会阻塞下来。

使用 thread | grep pool 命令查看线程池里线程信息。

上面的模拟代码里 deadThread方法实现了一个死锁,使用 thread -b 命令查看直接定位到死锁信息。

代码语言:javascript
复制
	/**
	 * 死锁
	 */
	private static void deadThread() {
		/** 创建资源 */
		Object resourceA = new Object();
		Object resourceB = new Object();
		// 创建线程
		Thread threadA = new Thread(() -> {
			synchronized (resourceA) {
				System.out.println(Thread.currentThread() + " get ResourceA");
				try {
					Thread.sleep(1000);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				System.out.println(Thread.currentThread() + "waiting get resourceB");
				synchronized (resourceB) {
					System.out.println(Thread.currentThread() + " get resourceB");
				}
			}
		});

		Thread threadB = new Thread(() -> {
			synchronized (resourceB) {
				System.out.println(Thread.currentThread() + " get ResourceB");
				try {
					Thread.sleep(1000);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				System.out.println(Thread.currentThread() + "waiting get resourceA");
				synchronized (resourceA) {
					System.out.println(Thread.currentThread() + " get resourceA");
				}
			}
		});
		threadA.start();
		threadB.start();
	}

4.4. 代码反编译

通过 jad ArthasTest 命令可以反编译代码。

4.5. 查看方法信息:

通过sm命令可以查看类中的所有方法信息。

4.6. 查看静态变量:

通过ognl ‘@ArthasTest@hashSet’ 命令可以查看 ArthasTest类的hashSet 静态变量的值。

4.7. 查看方法的调用耗时

先定义一个测试的方法,这里定义了UserController类以及UserServiceImpl类,UserController类作为接口的总入口。

代码语言:javascript
复制
import java.util.HashMap;

@RestController
@Slf4j
public class UserController {

	@Autowired
	private UserServiceImpl userService;

	@GetMapping(value = "/user")
	public HashMap<String, Object> getUser(Integer uid) throws Exception {
		log.info("------模拟用户查询,uid={}----", uid);
		// 模拟用户查询
		userService.get(uid);
		HashMap<String, Object> hashMap = new HashMap<>();
		hashMap.put("uid", uid);
		hashMap.put("name", "name" + uid);
		return hashMap;
	}
}

UserServiceImpl类作为业务实现。

代码语言:javascript
复制
package com.jay.demos.web;

import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

@Service
@Slf4j
public class UserServiceImpl {

	public void get(Integer uid) throws Exception {
		check(uid);
		service(uid);
		redis(uid);
		mysql(uid);
	}

	public void service(Integer uid) throws Exception {
		int count = 0;
		for (int i = 0; i < 10; i++) {
			count += i;
		}
		log.info("service  end {}", count);
	}

	public void redis(Integer uid) throws Exception {
		int count = 0;
		for (int i = 0; i < 10000; i++) {
			count += i;
		}
		log.info("redis  end {}", count);
	}

	public void mysql(Integer uid) throws Exception {
		long count = 0;
		for (int i = 0; i < 10000000; i++) {
			count += i;
		}
		log.info("mysql end {}", count);
	}

	public boolean check(Integer uid) throws Exception {
		if (uid == null || uid < 0) {
			log.error("uid不正确,uid:{}", uid);
			throw new Exception("uid不正确");
		}
		return true;
	}
}

如果要通过一个接口中,各部分的耗时,则可以使用: trace [类地址] [方法名] 。例如本例中的就是 trace com.jay.demos.web.UserController getUser

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2024-01-19,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 码农飞哥 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 2. Arthas的使用场景
    • 2.1 性能问题定位
      • 2.2 实时调试
        • 2.3 内存分析
          • 2.4 线上问题快速定位
          • 3. Arthas怎么使用
            • 3.1 下载安装
              • 3.2 运行Arthas
                • 3.4 使用Arthas命令
                  • 3.5 退出
                  • 4. Arthas的常用操作
                    • 4.1. 全局监控
                      • 4.2. 定位CPU最高的方法
                        • 4.3. 线程死锁
                          • 4.4. 代码反编译
                            • 4.5. 查看方法信息:
                              • 4.6. 查看静态变量:
                                • 4.7. 查看方法的调用耗时
                                相关产品与服务
                                命令行工具
                                腾讯云命令行工具 TCCLI 是管理腾讯云资源的统一工具。使用腾讯云命令行工具,您可以快速调用腾讯云 API 来管理您的腾讯云资源。此外,您还可以基于腾讯云的命令行工具来做自动化和脚本处理,以更多样的方式进行组合和重用。
                                领券
                                问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档