Arthas使用
<p> </p><h2>1. Arthas 介绍</h2>
<p><strong>Arthas</strong> 是阿里巴巴开源的 <strong>Java 诊断工具</strong>,用于:</p>
<ul>
<li>实时监控线上应用(无需重启)</li>
<li>快速定位 CPU、内存、线程问题</li>
<li>分析方法执行耗时、参数、返回值</li>
<li>支持 JDK 6+(包括 JDK 21)</li>
</ul>
<blockquote>
<p>✅ <strong>核心优势</strong>:<br>通过 JVM Attach 机制动态注入 Agent,<strong>不修改代码、不重启应用</strong>。</p>
</blockquote>
<hr>
<h2>2. Arthas 的使用(下载安装 + 启动 + 命令)</h2>
<h3>📥 下载</h3>
<pre><code class="language-bash"># 从 GitHub 获取最新版(推荐 4.1.5+)</code></pre>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">wget</span> https:<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">repo1.maven.org/maven2/com/taobao/arthas/arthas-boot/4.1.5/arthas-boot-4.1.5.jar</span></pre>
</div>
<p><span style="font-size: 1.17em">🔌 启动方式(关键!JDK 21 必须用 </span><code style="font-size: 1.17em">-cp .</code><span style="font-size: 1.17em">)</span></p>
<h4>✅ 正确启动方式(JDK 21 通用):</h4>
<div class="cnblogs_code">
<pre># <span style="color: rgba(128, 0, 128, 1)">1</span><span style="color: rgba(0, 0, 0, 1)">. 编译代码(确保有 .class)
cd D:\my</span>-project\jdk-<span style="color: rgba(128, 0, 128, 1)">21</span>-test-<span style="color: rgba(0, 0, 0, 1)">workspace\src
javac </span>-d ..\out\production\jdk-<span style="color: rgba(128, 0, 128, 1)">21</span>-<span style="color: rgba(0, 0, 0, 1)">test com\my\test\multiThread\ArthasTest.java
# </span><span style="color: rgba(128, 0, 128, 1)">2</span>. 用 -<span style="color: rgba(0, 0, 255, 1)">cp</span><span style="color: rgba(0, 0, 0, 1)"> . 启动应用(绕过模块化问题!)
cd D:\my</span>-project\jdk-<span style="color: rgba(128, 0, 128, 1)">21</span>-test-workspace\out\production\jdk-<span style="color: rgba(128, 0, 128, 1)">21</span>-<span style="color: rgba(0, 0, 0, 1)">test
java </span>-<span style="color: rgba(0, 0, 255, 1)">cp</span> . com.my.test.multiThread.ArthasTest</pre>
</div>
<p><span style="font-size: 1em">❌ 错误启动方式(JDK 21 会导致 Arthas 无法 attach):</span></p>
<div class="cnblogs_code">
<pre>java com.my.test.multiThread.ArthasTest# 无 -<span style="color: rgba(0, 0, 255, 1)">cp</span> . !</pre>
</div>
<p><span style="font-size: 1em">🌐 JDK 1.6 vs JDK 21 启动差异</span></p>
<table>
<thead>
<tr><th>JDK 版本</th><th>启动命令</th><th>说明</th></tr>
</thead>
<tbody>
<tr>
<td><strong>JDK 1.6</strong></td>
<td><code>java com.xxx.Main</code></td>
<td>无需 <code>-cp .</code>(无模块系统)</td>
</tr>
<tr>
<td><strong>JDK 21</strong></td>
<td><code>java -cp . com.xxx.Main</code></td>
<td><strong>必须加 -cp .</strong>(避免模块化干扰)</td>
</tr>
</tbody>
</table>
<h3>📌 常用命令(实战精选)</h3>
<table>
<thead>
<tr><th>命令</th><th>作用</th></tr>
</thead>
<tbody>
<tr>
<td><code>thread -n 1 -i 500</code></td>
<td>查看 CPU 最高线程(500ms 采样,时间可选)</td>
</tr>
<tr>
<td><code>jad com.my.test.multiThread.ArthasTest</code></td>
<td>反编译类(查看源码)</td>
</tr>
<tr>
<td><code>thread 1</code></td>
<td>直接查看主线程(ID=1)</td>
</tr>
<tr>
<td><code>dashboard(按 <code>q</code> 退出)</code></td>
<td>实时监控(CPU/内存/线程)</td>
</tr>
<tr>
<td><code>ognl '@com.my.test.multiThread.ArthasTest@counter'</code></td>
<td>查看静态变量(无 I/O)</td>
</tr>
</tbody>
</table>
<blockquote>
<p>💡 <strong>为什么 <code>thread -n 1</code> 常显示 Arthas 自身线程?</strong><br>因为 Arthas 本身在采样,<strong>用 <code>-i 500</code> 增加采样时间</strong>即可解决(见下文)。</p>
</blockquote>
<hr>
<h2>3. 举例说明</h2>
<h3>🔥 单线程 CPU 压测</h3>
<h4>✅ 步骤:</h4>
<p><strong>1. 修改代码</strong>(移除 <code>System.out.println</code>):</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">while</span> (<span style="color: rgba(0, 0, 255, 1)">true</span><span style="color: rgba(0, 0, 0, 1)">) {
counter</span>++; <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 仅保留纯计算</span>
}</pre>
</div>
<pre><strong><code class="language-bash">2. 启动</code></strong></pre>
<p> </p>
<p> </p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 0, 1)"># 进入 classpath 根目录
cd D:\my</span>-project\jdk-<span style="color: rgba(128, 0, 128, 1)">21</span>-test-workspace\out\production\jdk-<span style="color: rgba(128, 0, 128, 1)">21</span>-<span style="color: rgba(0, 0, 0, 1)">test
# 用 </span>-<span style="color: rgba(0, 0, 255, 1)">cp</span><span style="color: rgba(0, 0, 0, 1)"> . 启动
java </span>-<span style="color: rgba(0, 0, 255, 1)">cp</span> . com.my.test.multiThread.ArthasTest</pre>
</div>
<p> </p>
<p><strong>3. Arthas 诊断</strong>:</p>
<p><span style="color: rgba(255, 0, 0, 1)">定位到 arthas-boot.jar 的目录下</span></p>
<div class="cnblogs_code">
<pre>java -jar arthas-<span style="color: rgba(0, 0, 0, 1)">boot.jar
# 选择进程 ID(如 </span><span style="color: rgba(128, 0, 128, 1)">49827</span><span style="color: rgba(0, 0, 0, 1)">)
thread </span>-n <span style="color: rgba(128, 0, 128, 1)">1</span> -i <span style="color: rgba(128, 0, 128, 1)">500</span># 正确!</pre>
</div>
<p><strong>输出</strong>:</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">main</span><span style="color: rgba(128, 0, 0, 1)">"</span> Id=<span style="color: rgba(128, 0, 128, 1)">1</span> cpuUsage=<span style="color: rgba(128, 0, 128, 1)">99.2</span>%<span style="color: rgba(0, 0, 0, 1)"> ... RUNNABLE
at com.my.test.multiThread.ArthasTest.single(ArthasTest.java:</span><span style="color: rgba(128, 0, 128, 1)">21</span>)</pre>
</div>
<hr>
<h3>🌐 多线程示例(扩展)</h3>
<h4>1. 创建多线程类 <code>MultiThreadTest.java</code>:</h4>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">package</span><span style="color: rgba(0, 0, 0, 1)"> com.my.test.multiThread;
</span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">class</span><span style="color: rgba(0, 0, 0, 1)"> MultiThreadTest {
</span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">static</span> <span style="color: rgba(0, 0, 255, 1)">void</span><span style="color: rgba(0, 0, 0, 1)"> main(String[] args) {
</span><span style="color: rgba(0, 0, 255, 1)">for</span> (<span style="color: rgba(0, 0, 255, 1)">int</span> i = 0; i < 5; i++<span style="color: rgba(0, 0, 0, 1)">) {
</span><span style="color: rgba(0, 0, 255, 1)">new</span> Thread(() -><span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 0, 255, 1)">while</span> (<span style="color: rgba(0, 0, 255, 1)">true</span><span style="color: rgba(0, 0, 0, 1)">) {
System.out.println(</span>"Thread " +<span style="color: rgba(0, 0, 0, 1)"> Thread.currentThread().getId());
}
}).start();
}
}
}</span></pre>
</div>
<h4>2. 启动 & 诊断:</h4>
<div class="cnblogs_code">
<pre>cd D:\my-project\jdk-<span style="color: rgba(128, 0, 128, 1)">21</span>-test-workspace\out\production\jdk-<span style="color: rgba(128, 0, 128, 1)">21</span>-<span style="color: rgba(0, 0, 0, 1)">test
java </span>-<span style="color: rgba(0, 0, 255, 1)">cp</span> . com.my.test.multiThread.MultiThreadTest# 必须 -<span style="color: rgba(0, 0, 255, 1)">cp</span><span style="color: rgba(0, 0, 0, 1)"> .
# Arthas 诊断
thread </span>-n <span style="color: rgba(128, 0, 128, 1)">5</span> -i <span style="color: rgba(128, 0, 128, 1)">500</span></pre>
</div>
<p><strong>输出</strong>:</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">Thread-0</span><span style="color: rgba(128, 0, 0, 1)">"</span> Id=<span style="color: rgba(128, 0, 128, 1)">12</span> cpuUsage=<span style="color: rgba(128, 0, 128, 1)">20.3</span>%<span style="color: rgba(0, 0, 0, 1)"> ... RUNNABLE
at com.my.test.multiThread.MultiThreadTest.lambda$main$</span><span style="color: rgba(128, 0, 128, 1)">0</span>(MultiThreadTest.java:<span style="color: rgba(128, 0, 128, 1)">8</span><span style="color: rgba(0, 0, 0, 1)">)
</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">Thread-1</span><span style="color: rgba(128, 0, 0, 1)">"</span> Id=<span style="color: rgba(128, 0, 128, 1)">13</span> cpuUsage=<span style="color: rgba(128, 0, 128, 1)">20.1</span>%<span style="color: rgba(0, 0, 0, 1)"> ... RUNNABLE
at com.my.test.multiThread.MultiThreadTest.lambda$main$</span><span style="color: rgba(128, 0, 128, 1)">0</span>(MultiThreadTest.java:<span style="color: rgba(128, 0, 128, 1)">8</span>)</pre>
</div>
<hr>
<h2>4. 使用命令总结(按顺序)</h2>
<table>
<thead>
<tr><th>步骤</th><th>操作</th><th>关键命令</th><th>说明</th></tr>
</thead>
<tbody>
<tr>
<td>1. <strong>准备代码</strong></td>
<td>移除 <code>System.out.println</code>(避免 I/O 干扰)</td>
<td><code>counter++;</code></td>
<td><code>测试使用,可忽略</code></td>
</tr>
<tr>
<td>2. <strong>编译</strong></td>
<td>生成 .class 文件</td>
<td><code>javac -d out/production/... src/...</code></td>
<td><code>测试使用,可忽略</code></td>
</tr>
<tr>
<td>3. <strong>启动</strong></td>
<td><strong>必须加 <code>-cp .</code></strong>(JDK 21 必须)</td>
<td><code>java -cp . com.xxx.Main</code></td>
<td><code>测试使用,可忽略</code></td>
</tr>
<tr>
<td>4. <strong>启动 Arthas</strong></td>
<td>选择进程</td>
<td><code>java -jar arthas-boot.jar</code></td>
<td><code>实际使用</code></td>
</tr>
<tr>
<td>5. <strong>诊断 CPU</strong></td>
<td>用长采样时间</td>
<td><code>thread -n 1 -i 500</code></td>
<td><code>实际使用,thread -n 1 可查看占用内存最高的前1个线程</code></td>
</tr>
<tr>
<td>6. <strong>查看源码</strong></td>
<td>反编译类</td>
<td><code>jad com.xxx.Main</code></td>
<td> </td>
</tr>
<tr>
<td>7. <strong>查看变量</strong></td>
<td>无 I/O 查看</td>
<td><code>ognl '@Main@counter'</code></td>
<td> </td>
</tr>
</tbody>
</table>
<blockquote>
<p>✅ <strong>核心口诀</strong>:<br><strong><code>-cp .</code> 启动 + <code>-i 500</code> 采样 = 看到真实业务代码</strong></p>
</blockquote>
<hr>
<h2>5. Arthas 使用场景</h2>
<table>
<thead>
<tr><th>场景</th><th>举例</th><th>Arthas 命令</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>CPU 高负载</strong></td>
<td>死循环、计算密集型</td>
<td><code>thread -n 1 -i 500</code></td>
</tr>
<tr>
<td><strong>线程阻塞</strong></td>
<td>死锁、等待 I/O</td>
<td><code>thread -n 5</code> + <code>jstack</code></td>
</tr>
<tr>
<td><strong>方法耗时</strong></td>
<td>优化慢方法</td>
<td><code>trace com.xxx.Method</code></td>
</tr>
<tr>
<td><strong>类加载问题</strong></td>
<td>类冲突、重复加载</td>
<td><code>sc -d java.util.ArrayList</code></td>
</tr>
<tr>
<td><strong>线上调试</strong></td>
<td>不重启查变量</td>
<td><code>ognl '@Main@counter'</code></td>
</tr>
</tbody>
</table>
<hr>
<h2>6. Arthas 监控机制原理</h2>
<ol>
<li><strong>Attach 机制</strong>:
<ul>
<li>通过 JVM 的 <code>Attach API</code>(<code>jdk.attach</code> 模块)附加到目标进程。</li>
</ul>
</li>
<li><strong>Agent 注入</strong>:
<ul>
<li>Arthas 以 Java Agent 方式注入字节码(<code>arhats-agent.jar</code>)。</li>
</ul>
</li>
<li><strong>采样原理</strong>:
<ul>
<li>用 <code>ThreadMXBean</code> 获取线程 CPU 时间。</li>
<li>通过 <code>Sampling</code>(采样)统计 CPU 使用率(默认 100ms)。</li>
</ul>
</li>
<li><strong>堆栈解析</strong>:
<ul>
<li>从 JVM 获取线程栈 → 映射到源码行号(需 <code>.class</code> 文件有调试信息)。</li>
</ul>
</li>
</ol>
<blockquote>
<p>⚙️ <strong>为什么 <code>thread -n 1</code> 有时显示 Arthas 自身线程?</strong><br>因为采样过程本身会短暂占用 CPU,<strong>用 <code>-i 500</code> 增加采样时间</strong>即可平滑干扰。</p>
</blockquote>
<hr>
<h2>7. 注意事项</h2>
<table>
<thead>
<tr><th>事项</th><th>说明</th><th>解决方案</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>性能影响</strong></td>
<td>采样时 CPU +1%~3%</td>
<td>仅在诊断时使用,<strong>用完关闭</strong></td>
</tr>
<tr>
<td><strong>必须关闭</strong></td>
<td>长期运行会累积开销</td>
<td>退出 Arthas:按 <code>q</code> 或 <code>exit</code></td>
</tr>
<tr>
<td><strong>JDK 21 模块化</strong></td>
<td>无 <code>-cp .</code> 无法 attach</td>
<td><strong>启动命令必须加 <code>-cp .</code></strong></td>
</tr>
<tr>
<td><strong>I/O 干扰</strong></td>
<td><code>System.out.println</code> 会掩盖 CPU 问题</td>
<td><strong>压测时移除所有 I/O</strong></td>
</tr>
<tr>
<td><strong>线程名</strong></td>
<td>业务线程名需明确</td>
<td>用 <code>new Thread("MyThread")</code> 命名</td>
</tr>
</tbody>
</table>
<blockquote>
<p>⚠️ <strong>重要</strong>:<br><strong>Arthas 不是监控系统,是诊断工具</strong> —— 用完立刻退出,避免长期影响生产环境。</p>
</blockquote>
<hr>
<h2>8. 其他建议</h2>
<ol>
<li><strong>优先用 <code>dashboard</code></strong>:
<div class="cnblogs_code">
<pre>dashboard# 实时看 CPU/内存/线程,按 q 退出</pre>
</div>
</li>
<li><strong>避免在高负载时频繁使用</strong>:
<ul>
<li>先用 <code>thread -n 1 -i 500</code> 快速定位,再深入分析。</li>
</ul>
</li>
<li><strong>生产环境安全</strong>:
<ul>
<li>在测试环境验证 Arthas 命令,再上生产。</li>
</ul>
</li>
<li><strong>JDK 21 专用技巧</strong>:
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 0, 1)"># 启动时强制加载模块(备用方案)
java </span>-<span style="color: rgba(0, 0, 255, 1)">cp</span> . --add-modules java.instrument com.xxx.Main</pre>
</div>
<p><strong>自动化脚本</strong>:</p>
</li>
<li>写 <code>.bat</code> 脚本一键启动 + 诊断:
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 0, 1)">@echo off
cd </span>/d D:\my-project\out\production\jdk-<span style="color: rgba(128, 0, 128, 1)">21</span>-<span style="color: rgba(0, 0, 0, 1)">test
java </span>-<span style="color: rgba(0, 0, 255, 1)">cp</span> . com.my.test.multiThread.ArthasTest &<span style="color: rgba(0, 0, 0, 1)">
timeout </span>/t <span style="color: rgba(128, 0, 128, 1)">3</span><span style="color: rgba(0, 0, 0, 1)">
java </span>-jar arthas-boot.jar</pre>
</div>
</li>
</ol><hr>
<h2>✅ 最终总结</h2>
<blockquote>
<p><strong>Arthas 是 Java 线上问题的“手术刀”</strong>,但必须按 <strong>JDK 21 规则</strong>使用:<br><strong>启动命令加 <code>-cp .</code> → 诊断命令用 <code>-i 500</code> → 用完立刻退出</strong>。<br>你已成功解决模块化问题,现在可以 <strong>100% 诊断真实 CPU 问题</strong>!</p>
</blockquote>
<blockquote>
<p>💡 <strong>记住</strong>:<br><code>thread -n 1 -i 500</code> = 看到 <code>ArthasTest.java:21</code><br><code>thread -n 1</code> = 看到 <code>arthas-command-execute</code>(干扰!)</p>
</blockquote>
<p>现在你可以自信地用 Arthas 诊断任何 Java 问题了!🔥</p><br><br>
来源:https://www.cnblogs.com/yenengfeng/p/19508752
頁:
[1]