Skip to content

Commit 24f210c

Browse files
committed
[feat] update 手把手教你定位常见Java性能问题
1 parent f10a5c9 commit 24f210c

11 files changed

+376
-19
lines changed

README.md

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -222,13 +222,12 @@ Github用户如果访问速度缓慢的话,可以转移到[码云](https://git
222222

223223
#### Spring/SpringBoot
224224

225-
1. [Spring 学习与面试(待重构)](docs/system-design/framework/spring/Spring.md)
226-
2. **[Spring 常见问题总结](docs/system-design/framework/spring/SpringInterviewQuestions.md)**
227-
3. **[Spring/Spring常用注解总结!安排!](./docs/system-design/framework/spring/spring-annotations.md)**
228-
4. **[SpringBoot 指南/常见面试题总结](https://github.com/Snailclimb/springboot-guide)**
229-
5. [Spring中 Bean 的作用域与生命周期](docs/system-design/framework/spring/SpringBean.md)
230-
6. [SpringMVC 工作原理详解](docs/system-design/framework/spring/SpringMVC-Principle.md)
231-
7. [Spring中都用到了那些设计模式?](docs/system-design/framework/spring/Spring-Design-Patterns.md)
225+
1. **[Spring 常见问题总结](docs/system-design/framework/spring/SpringInterviewQuestions.md)**
226+
2. **[Spring/Spring常用注解总结!安排!](./docs/system-design/framework/spring/spring-annotations.md)**
227+
3. **[SpringBoot 指南/常见面试题总结](https://github.com/Snailclimb/springboot-guide)**
228+
4. [Spring中 Bean 的作用域与生命周期](docs/system-design/framework/spring/SpringBean.md)
229+
5. [SpringMVC 工作原理详解](docs/system-design/framework/spring/SpringMVC-Principle.md)
230+
6. [Spring中都用到了那些设计模式?](docs/system-design/framework/spring/Spring-Design-Patterns.md)
232231

233232
#### MyBatis
234233

Loading
Loading
Loading
Loading
Loading
Loading
Loading
Loading

docs/java/手把手教你定位常见Java性能问题.md

Lines changed: 16 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,6 @@ vmstat是一个指定周期和采集次数的虚拟内存检测工具,可以
3333
- wa: IO等待时间百分比
3434
- id: 空闲时间百分比
3535

36-
37-
3836
**pidstat命令**
3937

4038
pidstat 是 Sysstat 中的一个组件,也是一款功能强大的性能监测工具,`top``vmstat` 两个命令都是监测进程的内存、CPU 以及 I/O 使用情况,而 pidstat 命令可以检测到线程级别的。`pidstat`命令线程切换字段说明如下:
@@ -95,7 +93,13 @@ jmap也是JDK工具命令,他可以查看堆内存的初始化信息以及堆
9593

9694
**mat内存工具**
9795

98-
MAT(Memory Analyzer Tool)工具是eclipse的一个插件(MAT也可以单独使用),它分析大内存的dump文件时,可以非常直观的看到各个对象在堆空间中所占用的内存大小、类实例数量、对象引用关系、利用OQL对象查询,以及可以很方便的找出对象GC Roots的相关信息。[下载地址可以点击这里](https://www.eclipse.org/mat/downloads.php)
96+
MAT(Memory Analyzer Tool)工具是eclipse的一个插件(MAT也可以单独使用),它分析大内存的dump文件时,可以非常直观的看到各个对象在堆空间中所占用的内存大小、类实例数量、对象引用关系、利用OQL对象查询,以及可以很方便的找出对象GC Roots的相关信息。
97+
98+
**idea中也有这么一个插件,就是JProfiler**
99+
100+
相关阅读:
101+
102+
1. 《性能诊断利器 JProfiler 快速入门和最佳实践》:[https://segmentfault.com/a/1190000017795841](https://segmentfault.com/a/1190000017795841)
99103

100104
## 模拟环境准备
101105

@@ -106,7 +110,7 @@ MAT(Memory Analyzer Tool)工具是eclipse的一个插件(MAT也可以单独使
106110
模拟CPU占满还是比较简单,直接写一个死循环计算消耗CPU即可。
107111

108112
````java
109-
/**
113+
/**
110114
* 模拟CPU占满
111115
*/
112116
@GetMapping("/cpu/loop")
@@ -127,15 +131,15 @@ MAT(Memory Analyzer Tool)工具是eclipse的一个插件(MAT也可以单独使
127131

128132
请求接口地址测试`curl localhost:8080/cpu/loop`,发现CPU立马飙升到100%
129133

130-
![JshPzQ.png](https://s1.ax1x.com/2020/04/25/JshPzQ.png)
134+
![](./images/performance-tuning/java-performance1.png)
131135

132136
通过执行`top -Hp 32805` 查看Java线程情况
133137

134-
[![JsheoV.png](https://s1.ax1x.com/2020/04/25/JsheoV.png)](https://imgchr.com/i/JsheoV)
138+
![](./images/performance-tuning/java-performance2.png)
135139

136140
执行 `printf '%x' 32826` 获取16进制的线程id,用于`dump`信息查询,结果为 `803a`。最后我们执行`jstack 32805 |grep -A 20 803a `来查看下详细的`dump`信息。
137141

138-
![JshFMj.png](https://s1.ax1x.com/2020/04/25/JshFMj.png)
142+
![](./images/performance-tuning/java-performance3.png)
139143

140144
这里`dump`信息直接定位出了问题方法以及代码行,这就定位出了CPU占满的问题。
141145

@@ -168,23 +172,23 @@ java.lang.OutOfMemoryError: Java heap space
168172

169173
我们用`jstat -gc pid` 命令来看看程序的GC情况。
170174

171-
[![Jshkss.png](https://s1.ax1x.com/2020/04/25/Jshkss.png)](https://imgchr.com/i/Jshkss)
175+
![](./images/performance-tuning/java-performance4.png)
172176

173177
很明显,内存溢出了,堆内存经过45次 Full Gc 之后都没释放出可用内存,这说明当前堆内存中的对象都是存活的,有GC Roots引用,无法回收。那是什么原因导致内存溢出呢?是不是我只要加大内存就行了呢?如果是普通的内存溢出也许扩大内存就行了,但是如果是内存泄漏的话,扩大的内存不一会就会被占满,所以我们还需要确定是不是内存泄漏。我们之前保存了堆 Dump 文件,这个时候借助我们的MAT工具来分析下。导入工具选择`Leak Suspects Report`,工具直接就会给你列出问题报告。
174178

175-
![JshALn.png](https://s1.ax1x.com/2020/04/25/JshALn.png)
179+
![](./images/performance-tuning/java-performance5.png)
176180

177181
这里已经列出了可疑的4个内存泄漏问题,我们点击其中一个查看详情。
178182

179-
[![JshCRg.png](https://s1.ax1x.com/2020/04/25/JshCRg.png)](https://imgchr.com/i/JshCRg)
183+
![](./images/performance-tuning/java-performance6.png)
180184

181185
这里已经指出了内存被线程占用了接近50M的内存,占用的对象就是ThreadLocal。如果想详细的通过手动去分析的话,可以点击`Histogram`,查看最大的对象占用是谁,然后再分析它的引用关系,即可确定是谁导致的内存溢出。
182186

183-
![JshZd0.png](https://s1.ax1x.com/2020/04/25/JshZd0.png)
187+
![](./images/performance-tuning/java-performance7.png)
184188

185189
上图发现占用内存最大的对象是一个Byte数组,我们看看它到底被那个GC Root引用导致没有被回收。按照上图红框操作指引,结果如下图:
186190

187-
![JshniT.png](https://s1.ax1x.com/2020/04/25/JshniT.png)
191+
![](./images/performance-tuning/java-performance8.png)
188192

189193
我们发现Byte数组是被线程对象引用的,图中也标明,Byte数组对像的GC Root是线程,所以它是不会被回收的,展开详细信息查看,我们发现最终的内存占用对象是被ThreadLocal对象占据了。这也和MAT工具自动帮我们分析的结果一致。
190194

0 commit comments

Comments
 (0)