# 怎么排查Java系統運行緩慢等問題
## 目錄
- [一、問題現象與分類](#一問題現象與分類)
- [二、基礎環境檢查](#二基礎環境檢查)
- [三、JVM性能分析](#三jvm性能分析)
- [四、線程與鎖分析](#四線程與鎖分析)
- [五、數據庫性能排查](#五數據庫性能排查)
- [六、外部依賴與IO瓶頸](#六外部依賴與io瓶頸)
- [七、代碼級優化建議](#七代碼級優化建議)
- [八、監控體系建設](#八監控體系建設)
- [九、典型場景案例](#九典型場景案例)
- [十、總結與預防措施](#十總結與預防措施)
---
## 一、問題現象與分類
### 1.1 常見表現形態
- **響應時間延長**:API接口從200ms增加到2s
- **吞吐量下降**:TPS從1000驟降到200
- **資源占用飆升**:CPU持續>90%或內存占用超過80%
- **異常波動**:白天正常但夜間定時變慢
### 1.2 問題分類矩陣
| 問題類型 | 特征指標 | 典型場景 |
|----------------|-------------------------|-----------------------|
| CPU密集型 | CPU利用率>80% | 復雜計算/死循環 |
| IO密集型 | IO等待>30% | 數據庫慢查詢/日志寫入 |
| 內存泄漏 | Old Gen持續增長 | 靜態集合緩存 |
| 鎖競爭 | 線程BLOCKED狀態增多 | 同步方法濫用 |
| 外部依賴瓶頸 | 網絡延遲突增 | 第三方API超時 |
---
## 二、基礎環境檢查
### 2.1 系統資源監控
```bash
# Linux系統檢查
top -H -p <java_pid> # 查看進程級資源占用
vmstat 1 5 # 檢查CPU/IO/memory上下文切換
sar -n DEV 1 3 # 網絡流量分析
// 獲取運行時參數
Runtime.getRuntime().availableProcessors(); // 核心數
ManagementFactory.getRuntimeMXBean().getInputArguments(); // JVM參數
jmap -histo:live <pid> # 對象直方圖
jmap -dump:format=b,file=heap.hprof <pid> # 堆轉儲
jstat -gcutil <pid> 1000 5 # GC實時監控
# 推薦GC日志參數
-XX:+PrintGCDetails
-XX:+PrintGCDateStamps
-Xloggc:/path/to/gc.log
jstack <pid> > thread_dump.txt
# 使用VisualVM或fastthread.io在線分析
// 示例:死鎖代碼
public class DeadLock {
static Object lock1 = new Object();
static Object lock2 = new Object();
public static void main(String[] args) {
new Thread(() -> {
synchronized (lock1) {
sleep(100);
synchronized (lock2) {}
}
}).start();
new Thread(() -> {
synchronized (lock2) {
sleep(100);
synchronized (lock1) {}
}
}).start();
}
}
ConcurrentHashMap替代synchronizedMapReadWriteLock替代重量級鎖ThreadLocal減少競爭-- MySQL慢查詢日志
SET GLOBAL slow_query_log = ON;
SET GLOBAL long_query_time = 1;
-- 執行計劃分析
EXPLN ANALYZE SELECT * FROM large_table WHERE unindexed_column = 'value';
# Spring Boot配置示例
spring:
datasource:
hikari:
maximum-pool-size: 20
connection-timeout: 30000
leak-detection-threshold: 5000
// HttpClient連接池配置
PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager();
cm.setMaxTotal(200);
cm.setDefaultMaxPerRoute(50);
FileChannel替代傳統IOMappedByteBufferAsyncAppender// 反面案例 - 動態擴容
List<User> users = new ArrayList<>();
for(int i=0; i<1e6; i++){
users.add(getUser(i)); // 觸發多次擴容
}
// 優化方案
List<User> users = new ArrayList<>( (int)1e6 );
// 低效寫法
list.stream()
.filter(x -> x>10)
.collect(Collectors.toList())
.forEach(System.out::println);
// 優化為終端操作
list.stream()
.filter(x -> x>10)
.forEach(System.out::println);
| 層級 | 必監控指標 | 工具示例 |
|---|---|---|
| JVM | GC時間/堆內存/線程數 | Prometheus+Grafana |
| 數據庫 | 慢查詢/連接數/鎖等待 | Datadog |
| 外部調用 | 成功率/TP99/重試次數 | SkyWalking |
現象:秒殺活動開始后系統卡死
根因:
1. 庫存查詢SQL未走索引
2. 同步鎖導致線程堆積
3. Redis連接池耗盡
解決方案:
1. 添加stock_id索引
2. 改用Redis分布式鎖
3. 連接池擴容+預熱
”`
(注:此為精簡框架,完整8000字版本需擴展各章節案例分析、數據圖表、工具截圖及具體參數調優建議)
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。