前言

💡 痛点:服务器 CPU 突然打满,业务响应缓慢?内存持续增长,不知道哪里泄漏?程序运行一段时间后越来越卡?

🎯 解决方案:掌握 CPU 与内存问题排查 — 从系统监控、进程分析、到问题定位与解决。

CPU/内存问题排查流程图:

CPU

内存

性能问题

CPU还是内存

查看负载

查看使用

定位进程

定位进程

定位线程

分析内存

分析代码

修复问题

常见性能问题类型:

类型 表现 原因
CPU 100% 所有请求变慢 死循环/密集计算
CPU 单核 100% 单线程瓶颈 GIL/锁竞争
内存泄漏 内存持续增长 对象未释放
内存溢出 OOM 崩溃 内存持续增长

一、CPU 问题排查

1.1 系统 CPU 监控

# ===== 系统 CPU 监控 =====

# 1. top - 实时监控(按 CPU 排序)
top
# 按 P 键:按 CPU 使用率排序
# 按 M 键:按内存排序
# 按 1 键:显示每个 CPU 核心
# 按 q 键:退出

# 2. htop - 更友好的 top(如果可用)
htop
# F6: 排序
# F5: 树形视图
# F3: 搜索

# 3. mpstat - 多核 CPU 使用率
mpstat -P ALL 1 5
# -P ALL: 所有 CPU
# 1: 每秒更新
# 5: 共 5 次

# 输出示例:
# 09:00:00 AM  CPU    %usr   %nice    %sys %iowait    %irq   %soft  %steal  %guest  %gnice   %idle
# 09:00:01 AM  all    5.23    0.00    1.45    0.12    0.00    0.05    0.00    0.00    0.00   93.15
# 09:00:01 AM    0    3.12    0.00    0.89    0.05    0.00    0.02    0.00    0.00    0.00   95.92
# 09:00:01 AM    1    7.34    0.00    2.01    0.20    0.00    0.08    0.00    0.00    0.00   90.37

# 4. sar - 系统活动报告
sar -u 1 10       # CPU 使用率
sar -P ALL 1 10   # 每个 CPU 使用率
sar -q 1 10       # 负载情况

# 5. uptime - 快速查看负载
uptime
# 输出:09:00:00 up 10 days,  3:22,  2 users,  load average: 5.23, 4.56, 3.21
# load average: 1分钟, 5分钟, 15分钟负载

# 6. Python 脚本监控 CPU
#!/usr/bin/env python3
"""CPU 监控脚本"""

import psutil
import time
from datetime import datetime

def monitor_cpu(interval=1, duration=60):
    """监控 CPU 使用率"""
    print(f"{'时间':<20} {'CPU%':<10} {'每个核%':<40}")
    print("-" * 70)
    
    start = time.time()
    while time.time() - start < duration:
        cpu_percent = psutil.cpu_percent(interval=interval)
        cpu_per_core = psutil.cpu_percent(interval=0, percpu=True)
        
        now = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
        cores = ", ".join([f"C{i}:{p:.1f}%" for i, p in enumerate(cpu_per_core)])
        
        print(f"{now:<20} {cpu_percent:<10.1f} {cores:<40}")
        
        if time.time() - start >= duration:
            break

def get_high_cpu_processes(top_n=10):
    """获取高 CPU 进程"""
    processes = []
    for p in psutil.process_iter(['pid', 'name', 'cpu_percent', 'memory_percent']):
        try:
            pinfo = p.info
            cpu = pinfo['cpu_percent']
            if cpu > 0:
                processes.append(pinfo)
        except (psutil.NoSuchProcess, psutil.AccessDenied):
            pass
    
    # 按 CPU 排序
    processes.sort(key=lambda x: x['cpu_percent'], reverse=True)
    
    print(f"\nTop {top_n} CPU 进程:")
    print(f"{'PID':<10} {'名称':<30} {'CPU%':<10} {'内存%':<10}")
    print("-" * 60)
    
    for proc in processes[:top_n]:
        print(f"{proc['pid']:<10} {proc['name']:<30} {proc['cpu_percent']:<10.1f} {proc['memory_percent']:<10.2f}")

if __name__ == '__main__':
    import sys
    
    if len(sys.argv) > 1 and sys.argv[1] == 'top':
        get_high_cpu_processes()
    else:
        monitor_cpu(interval=2, duration=30)

1.2 进程 CPU 分析

# ===== 进程 CPU 分析 =====

# 1. top 查看指定进程
top -p <PID>
top -p 12345

# 2. ps 查看进程 CPU
ps aux | sort -k3 -rn | head -20
ps -eo pid,ppid,%cpu,%mem,cmd | sort -k3 -rn | head -20

# 3. pidstat - 进程级 CPU 统计(需要 sysstat)
pidstat -p 12345 1 5    # 监控指定进程
pidstat -u 1 5          # 所有进程 CPU
pidstat -r 1 5          # 所有进程内存

# 4. 查看进程的线程 CPU
ps -T -p <PID>
# -T: 显示线程
# SPID: 线程 ID
# CMD: 线程名

# 5. top 查看线程
top -H -p <PID>
# -H: 显示线程
# 按 Shift+H 切换线程/进程视图

# 6. 查看进程的打开文件
lsof -p <PID>

# 7. strace 跟踪系统调用(CPU 高时使用,谨慎)
strace -p <PID> -c    # 统计系统调用
strace -p <PID> -f    # 跟踪子进程
strace -p <PID> -T    # 显示调用时间

# 8. perf 性能分析
perf top -p <PID>     # 实时分析
perf record -p <PID>   # 记录数据
perf report            # 查看报告

1.3 CPU 高原因分析

单核

多核

CPU 100%

单核还是多核

单线程问题

多线程/多进程问题

GIL 锁竞争

死循环

密集计算

锁竞争

上下文切换

频繁 GC

场景 表现 原因
死循环 CPU 单核 100% 代码逻辑错误
GIL 锁 Python 单核 多线程竞争 GIL
密集计算 CPU 持续高 算法复杂度高
锁竞争 多核同时高 锁粒度过粗
上下文切换 大量线程 线程数过多

二、内存问题排查

2.1 系统内存监控

# ===== 系统内存监控 =====

# 1. free - 查看内存使用
free -h
# -h: 人类可读格式
# 输出:
#               total        used        free      shared  buff/cache   available
# Mem:           31Gi       15Gi       2.3Gi       1.2Gi        13Gi        14Gi
# Swap:         8.0Gi       0B         8.0Gi

# 2. vmstat - 虚拟内存统计
vmstat 1 10
# procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----
#  r  b   swpd   free   buff  cache   si   so    bi    bo   in   cs us sy id wa st
#  1  0      0 2354688  524288 134217728    0    0     0    0   100  200  5  2 93  0  0

# 3. /proc/meminfo - 详细内存信息
cat /proc/meminfo
# MemTotal:       32768000 kB
# MemFree:         2354688 kB
# MemAvailable:   14680064 kB
# Buffers:          524288 kB
# Cached:         134217728 kB
# SwapCached:            0 kB
# ...

# 4. smem - 内存报告(如果可用)
smem -r -k
# -r: 按内存排序
# -k: 显示单位

# 5. Python 内存监控
#!/usr/bin/env python3
"""内存监控脚本"""

import psutil
import time
from datetime import datetime

def monitor_memory(interval=2, duration=60):
    """监控内存使用"""
    print(f"{'时间':<20} {'总量':<12} {'已用':<12} {'可用':<12} {'使用率':<10} {'Swap':<12}")
    print("-" * 78)
    
    start = time.time()
    while time.time() - start < duration:
        mem = psutil.virtual_memory()
        swap = psutil.swap_memory()
        
        now = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
        
        print(f"{now:<20} "
              f"{format_bytes(mem.total):<12} "
              f"{format_bytes(mem.used):<12} "
              f"{format_bytes(mem.available):<12} "
              f"{mem.percent:<10.1f} "
              f"{format_bytes(swap.used):<12}")
        
        if time.time() - start >= duration:
            break
        time.sleep(interval)

def format_bytes(bytes_value):
    """格式化字节"""
    for unit in ['B', 'KB', 'MB', 'GB', 'TB']:
        if bytes_value < 1024.0:
            return f"{bytes_value:.1f}{unit}"
        bytes_value /= 1024.0
    return f"{bytes_value:.1f}PB"

def get_high_memory_processes(top_n=10):
    """获取高内存进程"""
    processes = []
    for p in psutil.process_iter(['pid', 'name', 'memory_info', 'memory_percent']):
        try:
            pinfo = p.info
            mem_rss = pinfo['memory_info'].rss if pinfo['memory_info'] else 0
            processes.append({
                'pid': pinfo['pid'],
                'name': pinfo['name'],
                'memory_rss': mem_rss,
                'memory_percent': pinfo['memory_percent']
            })
        except (psutil.NoSuchProcess, psutil.AccessDenied):
            pass
    
    # 按内存排序
    processes.sort(key=lambda x: x['memory_rss'], reverse=True)
    
    print(f"\nTop {top_n} 内存进程:")
    print(f"{'PID':<10} {'名称':<30} {'RSS':<15} {'内存%':<10}")
    print("-" * 65)
    
    for proc in processes[:top_n]:
        print(f"{proc['pid']:<10} {proc['name']:<30} "
              f"{format_bytes(proc['memory_rss']):<15} {proc['memory_percent']:<10.2f}")

if __name__ == '__main__':
    import sys
    
    if len(sys.argv) > 1 and sys.argv[1] == 'top':
        get_high_memory_processes()
    else:
        monitor_memory(interval=2, duration=30)

2.2 内存泄漏检测

# ===== 内存泄漏检测 =====

# 1. valgrind - 内存泄漏检测(Linux)
valgrind --leak-check=full --show-leak-kinds=all --track-origins=yes ./your_program

# 2. gdb + gcore - 调试时生成 core dump
gdb -p <PID>
# (gdb) generate-core-file
# (gdb) quit

# 3. pmap - 查看进程内存映射
pmap -x <PID>
# -x: 扩展格式

# 4. 查看 /proc/<PID>/smaps(详细内存信息)
cat /proc/<PID>/smaps | grep -A 10 "heap"
# 或使用 smem
cat /proc/<PID>/smaps_rollup

# 5. Python 内存泄漏检测
#!/usr/bin/env python3
"""Python 内存泄漏检测"""

import gc
import sys
import tracemalloc
import psutil
import os
import time

def get_memory_usage():
    """获取当前进程内存使用"""
    process = psutil.Process(os.getpid())
    mem_info = process.memory_info()
    return {
        'rss': mem_info.rss,  # 物理内存
        'vms': mem_info.vms,  # 虚拟内存
        'rss_mb': mem_info.rss / 1024 / 1024,
        'vms_mb': mem_info.vms / 1024 / 1024
    }

def track_memory_leak():
    """跟踪内存增长"""
    print("内存泄漏跟踪测试")
    print("=" * 60)
    
    # 记录初始内存
    gc.collect()
    initial = get_memory_usage()
    print(f"初始内存: {initial['rss_mb']:.2f} MB")
    
    # 创建一些对象但不释放
    leaked_objects = []
    
    for i in range(100):
        # 创建大对象
        data = [x for x in range(10000)]
        leaked_objects.append(data)
        
        if i % 10 == 0:
            gc.collect()
            current = get_memory_usage()
            growth = current['rss_mb'] - initial['rss_mb']
            print(f"第 {i:3d} 次: {current['rss_mb']:.2f} MB (增长: +{growth:.2f} MB)")
        
        time.sleep(0.1)
    
    # 最终内存
    gc.collect()
    final = get_memory_usage()
    total_growth = final['rss_mb'] - initial['rss_mb']
    print(f"\n最终内存: {final['rss_mb']:.2f} MB")
    print(f"总增长: +{total_growth:.2f} MB")
    print(f"泄漏对象数: {len(leaked_objects)}")

def trace_memory():
    """使用 tracemalloc 跟踪内存"""
    print("\n使用 tracemalloc 跟踪内存分配")
    print("=" * 60)
    
    tracemalloc.start()
    
    # 记录初始状态
    snapshot1 = tracemalloc.take_snapshot()
    
    # 执行一些操作
    data = []
    for i in range(1000):
        data.append([x for x in range(100)])
    
    # 记录最终状态
    snapshot2 = tracemalloc.take_snapshot()
    
    # 对比差异
    top_stats = snapshot2.compare_to(snapshot1, 'lineno')
    
    print("\n内存增长 Top 10:")
    for stat in top_stats[:10]:
        print(stat)

def find_memory_leaks():
    """查找可疑的内存泄漏"""
    print("\n查找内存泄漏对象")
    print("=" * 60)
    
    # 强制垃圾回收
    gc.collect()
    
    # 获取所有不可达对象
    unreachable = gc.collect()
    print(f"回收了 {unreachable} 个不可达对象")
    
    # 查看垃圾对象
    garbage = gc.garbage
    print(f"垃圾对象数量: {len(garbage)}")
    
    # 按类型统计
    types = {}
    for obj in gc.get_objects():
        obj_type = type(obj).__name__
        types[obj_type] = types.get(obj_type, 0) + 1
    
    print("\n对象类型统计 (Top 20):")
    sorted_types = sorted(types.items(), key=lambda x: x[1], reverse=True)
    for obj_type, count in sorted_types[:20]:
        print(f"  {obj_type}: {count}")

if __name__ == '__main__':
    track_memory_leak()
    trace_memory()
    find_memory_leaks()

2.3 Go 内存泄漏检测

// Go 内存泄漏检测

package main

import (
    "fmt"
    "net/http"
    _ "net/http/pprof"
    "runtime"
    "time"
)

// 模拟泄漏的全局变量
var globalCache = make(map[string][]byte)
var leakSlice [][]byte

func main() {
    // 启动 pprof 服务器
    go func() {
        http.ListenAndServe("localhost:6060", nil)
    }()
    
    // 定期打印内存统计
    ticker := time.NewTicker(5 * time.Second)
    defer ticker.Stop()
    
    var m runtime.MemStats
    
    for {
        <-ticker.C
        runtime.ReadMemStats(&m)
        
        fmt.Printf("Alloc = %v MiB\t", m.Alloc/1024/1024)
        fmt.Printf("TotalAlloc = %v MiB\t", m.TotalAlloc/1024/1024)
        fmt.Printf("Sys = %v MiB\t", m.Sys/1024/1024)
        fmt.Printf("NumGC = %v\n", m.NumGC)
    }
}

// pprof 使用方法:
// 1. 访问 http://localhost:6060/debug/pprof/heap
// 2. 使用 go tool pprof 分析:
//    go tool pprof http://localhost:6060/debug/pprof/heap
// 3. 在 pprof 中输入:
//    top -cum    // 按累计内存排序
//    list <func>  // 查看函数内存分配

三、火焰图分析

3.1 perf +火焰图

# ===== perf + 火焰图 =====

# 1. 安装火焰图工具
# git clone https://github.com/brendangregg/FlameGraph.git
# export PATH=$PATH:/path/to/FlameGraph

# 2. 记录 CPU 数据
perf record -F 99 -p <PID> -g -- sleep 30
# -F 99: 采样频率 99Hz
# -p <PID>: 监控指定进程
# -g: 记录调用栈
# sleep 30: 持续 30 秒

# 3. 生成报告
perf report

# 4. 生成火焰图
perf script -i perf.data > perf.unfold
./stackcollapse-perf.pl perf.unfold > perf.folded
./flamegraph.pl perf.folded > cpu_flamegraph.svg

# 5. 监控整个系统
perf record -F 99 -ag -- sleep 60
perf script | ./inferno-collapse-perf > out.folded
./flamegraph.pl out.folded > system_flamegraph.svg

3.2 py-spy 火焰图

# ===== py-spy 火焰图 =====

# 安装
# pip install py-spy

# 记录 CPU 火焰图
py-spy record -o profile.svg --pid <PID>
# 或
py-spy record -o profile.svg -- python your_script.py

# 记录内存火焰图
py-spy record -o memory.svg --memory --pid <PID>

# 查看实时采样
py-spy top --pid <PID>

3.3 Node.js 火焰图

# ===== Node.js 火焰图 =====

# 1. 使用 clinic.js
# npm install -g clinic

# 2. CPU 分析
clinic doctor -- node server.js

# 3. 火焰图
clinic flame -- node server.js

# 4. 手动生成火焰图
# 启动服务器
node --prof server.js

# 模拟负载
# ab -n 1000 -c 10 http://localhost:3000/

# 处理日志
node --prof-process isolate-*.log | flamegraph | svg

3.4 Java 火焰图

# ===== Java 火焰图 =====

# 1. 使用 async-profiler
# 下载: https://github.com/jvm-profiling-tools/async-profiler

# 2. 记录 CPU 火焰图
./profiler.sh -d 30 -f profile.svg -e cpu <PID>

# 3. 记录内存分配
./profiler.sh -d 30 -f profile.svg -e alloc -i 10% <PID>

# 4. 查看运行中的 Java 进程
jps -l
# 输出: 12345 com.example.MyApplication

四、常见问题场景

4.1 Python GIL 问题

# ===== Python GIL 问题 =====

# 问题:多线程 CPU 密集型任务不加速

# 错误示例
import threading
import time

def cpu_task(n):
    """CPU 密集型任务"""
    result = 0
    for i in range(n):
        result += i * i
    return result

# 使用多线程(不加速!)
threads = []
start = time.time()
for _ in range(4):
    t = threading.Thread(target=cpu_task, args=(10000000,))
    threads.append(t)
    t.start()
for t in threads:
    t.join()
print(f"多线程耗时: {time.time() - start:.2f}s")  # ~4s

# 正确方案 1:多进程
import multiprocessing

def cpu_task_mp(n):
    return sum(i * i for i in range(n))

if __name__ == '__main__':
    with multiprocessing.Pool(4) as pool:
        start = time.time()
        results = pool.map(cpu_task_mp, [10000000] * 4)
        print(f"多进程耗时: {time.time() - start:.2f}s")  # ~1s

# 正确方案 2:使用 C 扩展
# 将 CPU 密集型代码放到 Cython 或 C 中

# 正确方案 3:asyncio + 线程池(IO 密集)
import asyncio
from concurrent.futures import ThreadPoolExecutor

async def io_task():
    # IO 操作
    await asyncio.sleep(1)
    return "done"

async def main():
    loop = asyncio.get_event_loop()
    with ThreadPoolExecutor(max_workers=4) as executor:
        tasks = [loop.run_in_executor(executor, blocking_io_task) for _ in range(10)]
        results = await asyncio.gather(*tasks)

4.2 Java 内存泄漏

// ===== Java 内存泄漏场景 =====

import java.util.*;
import java.lang.ref.WeakReference;

public class MemoryLeakExamples {
    
    // 泄漏 1:静态集合持有对象
    static List<Object> cache = new ArrayList<>();
    
    // 泄漏 2:未关闭的资源
    public void leakExample1() {
        // 文件流未关闭
        FileInputStream fis = new FileInputStream("file.txt");
        // 如果不在 finally 中关闭,异常时泄漏
    }
    
    // 泄漏 3:监听器未移除
    public void leakExample2() {
        // 添加监听器但从不移除
        button.addActionListener(e -> {/* ... */});
        // 导致 button 持有 listener 引用,listener 持有外部类引用
    }
    
    // 泄漏 4:String.intern()
    public void leakExample3() {
        // 大量调用 intern() 可能导致字符串常量池过大
        String s = new String("large_string").intern();
    }
    
    // 泄漏 5:ThreadLocal 未清理
    ThreadLocal<byte[]> buffer = new ThreadLocal<>();
    
    // 正确的做法
    public void correctPattern() {
        try {
            // 使用资源
        } finally {
            // 确保清理
            buffer.remove();
        }
    }
    
    // 使用 WeakReference 避免内存泄漏
    public void correctWithWeakRef() {
        // 弱引用可以被 GC 回收
        WeakReference<Object> ref = new WeakReference<>(new Object());
        // 当没有其他强引用时,对象会被回收
    }
}

4.3 Node.js 内存泄漏

// Node.js 内存泄漏

// 泄漏 1:全局变量
global.someCache = new Map();  // 永不释放

// 泄漏 2:闭包引用
function createLeak() {
    const largeArray = new Array(1000000);
    
    return function() {
        // 闭包引用 largeArray
        return largeArray.length;
    };
}

const leakedFn = createLeak();
// largeArray 永远不会被释放

// 泄漏 3:事件监听器累积
class EventEmitter {
    constructor() {
        this.events = {};
    }
    
    on(event, listener) {
        if (!this.events[event]) {
            this.events[event] = [];
        }
        this.events[event].push(listener);
        // 从不清理!
    }
}

// 泄漏 4:缓存未设置上限
const cache = new Map();
function getData(key) {
    if (!cache.has(key)) {
        cache.set(key, fetchFromDB(key));
    }
    return cache.get(key);
}
// cache 无限增长

// 正确的缓存实现
const LRU = require('lru-cache');
const cache = new LRU({ max: 1000 });  // 限制大小

// 使用 heapdump 分析
const heapdump = require('heapdump');

// 生成快照
heapdump.writeSnapshot('./heapdump.heapsnapshot');

// 在代码中
process.on('SIGUSR2', () => {
    heapdump.writeSnapshot();
});

五、生产案例

5.1 案例:Python 内存泄漏

# ===== 案例:Python 内存泄漏修复 =====

# 问题:服务运行几天后内存持续增长,最终 OOM

# 1. 复现问题
import requests
import time
import tracemalloc

tracemalloc.start()

def process_request():
    # 模拟处理请求
    data = requests.get('https://api.example.com/data').json()
    
    # 问题代码:每次请求都保存结果
    global results
    results.append(data)  # 结果永远不删除!
    
    return len(results)

# 测试
results = []
for i in range(100):
    process_request()
    if i % 10 == 0:
        current, peak = tracemalloc.get_traced_memory()
        print(f"请求 {i}: 内存 {current / 1024 / 1024:.1f} MB, 峰值 {peak / 1024 / 1024:.1f} MB")

# 诊断:使用 memory_profiler
# pip install memory_profiler
# mprof run python script.py
# mprof plot

# 2. 修复方案
from functools import lru_cache
from collections import deque

class RequestProcessor:
    def __init__(self, max_size=1000):
        # 使用有界队列
        self.results = deque(maxlen=max_size)
        self.cache = lru_cache(maxsize=1000)
    
    def process_request(self, request_id):
        # 使用缓存
        cache_key = f"request:{request_id}"
        
        if cache_key in self.cache:
            return self.cache(cache_key)
        
        # 处理请求
        data = self._fetch_data(request_id)
        
        # 有界存储
        self.results.append(data)
        
        # 有界缓存
        self.cache(cache_key, data)
        
        return data
    
    @lru_cache(maxsize=1000)
    def cache(self, key, value):
        return value
    
    def _fetch_data(self, request_id):
        # 模拟数据获取
        return {'id': request_id, 'data': 'x' * 1000}

# 3. 使用弱引用
import weakref

class Cache:
    def __init__(self):
        self._cache = {}
    
    def get(self, key):
        return self._cache.get(key)
    
    def set(self, key, value):
        # 使用弱引用,允许 GC
        self._cache[key] = weakref.ref(value)

# 4. 定期清理
import gc

class PeriodicCleaner:
    def __init__(self, interval=3600):
        self.interval = interval
        self.last_clean = time.time()
    
    def check(self):
        now = time.time()
        if now - self.last_clean > self.interval:
            gc.collect()
            self.last_clean = now
            print("执行了 GC 清理")

5.2 案例:Java 线程池泄漏

// 案例:线程池配置不当导致内存泄漏

import java.util.concurrent.*;
import java.util.*;

public class ThreadPoolLeak {
    
    // 错误:线程池无界
    ExecutorService badPool = Executors.newCachedThreadPool();
    
    // 正确:有界队列 + 有界线程池
    ExecutorService goodPool = new ThreadPoolExecutor(
        4,                      // corePoolSize
        8,                      // maxPoolSize
        60L, TimeUnit.SECONDS,  // keepAliveTime
        new LinkedBlockingQueue<>(100),  // 有界队列
        new ThreadPoolExecutor.CallerRunsPolicy()  // 拒绝策略
    );
    
    // 泄漏场景:不等待任务完成就关闭
    public void badShutdown() {
        ExecutorService pool = Executors.newFixedThreadPool(4);
        
        for (int i = 0; i < 100; i++) {
            final int taskId = i;
            pool.submit(() -> {
                try {
                    Thread.sleep(1000);
                    System.out.println("Task " + taskId + " done");
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                }
            });
        }
        
        pool.shutdown();  // 不等待完成就关闭!
        // 正在执行的任务被中断,可能导致资源泄漏
    }
    
    // 正确:等待任务完成
    public void goodShutdown() {
        ExecutorService pool = Executors.newFixedThreadPool(4);
        
        try {
            for (int i = 0; i < 100; i++) {
                final int taskId = i;
                pool.submit(() -> {
                    // 任务逻辑
                });
            }
        } finally {
            pool.shutdown();
            try {
                if (!pool.awaitTermination(60, TimeUnit.SECONDS)) {
                    pool.shutdownNow();
                }
            } catch (InterruptedException e) {
                pool.shutdownNow();
                Thread.currentThread().interrupt();
            }
        }
    }
    
    // 内存泄漏:ThreadLocal 未清理
    static ThreadLocal<List<Object>> threadLocalData = new ThreadLocal<>();
    
    public static void wrong() {
        // 使用但从不清理
        threadLocalData.set(new ArrayList<>());
        // ThreadLocalMap 中的 Entry 持有对象引用
    }
    
    public static void correct() {
        try {
            threadLocalData.set(new ArrayList<>());
            // 使用
        } finally {
            // 清理!
            threadLocalData.remove();
        }
    }
}

5.3 案例:Redis 连接泄漏

# 案例:Redis 连接泄漏

import redis
import time
import threading

# 错误:每次请求创建新连接
def bad_get_user(user_id):
    r = redis.Redis(host='localhost', port=6379)  # 每次都新建!
    return r.get(f'user:{user_id}')

# 错误:在循环中创建连接
def bad_batch_get(user_ids):
    results = []
    for user_id in user_ids:
        r = redis.Redis(host='localhost', port=6379)  # 泄漏!
        results.append(r.get(f'user:{user_id}'))
    return results

# 正确:使用连接池
class UserCache:
    def __init__(self):
        # 复用连接池
        self.pool = redis.ConnectionPool(
            host='localhost',
            port=6379,
            max_connections=50,
            decode_responses=True
        )
    
    def get_user(self, user_id):
        r = redis.Redis(connection_pool=self.pool)
        return r.get(f'user:{user_id}')
    
    def batch_get(self, user_ids):
        r = redis.Redis(connection_pool=self.pool)
        pipe = r.pipeline()
        
        for user_id in user_ids:
            pipe.get(f'user:{user_id}')
        
        return pipe.execute()

# 正确:在多线程环境中使用单例
class RedisClient:
    _instance = None
    _lock = threading.Lock()
    
    def __new__(cls):
        if cls._instance is None:
            with cls._lock:
                if cls._instance is None:
                    cls._instance = super().__new__(cls)
                    cls._instance.pool = redis.ConnectionPool(
                        host='localhost',
                        port=6379,
                        max_connections=50
                    )
        return cls._instance
    
    @property
    def client(self):
        return redis.Redis(connection_pool=self.pool)

# 使用
client = RedisClient()
user_data = client.client.get('user:123')

六、监控与告警

6.1 Prometheus 监控

# ===== Prometheus 监控配置 =====

# node_exporter 指标
# - node_cpu_seconds_total
# - node_memory_MemAvailable_bytes
# - node_memory_MemTotal_bytes
# - process_cpu_seconds_total
# - process_resident_memory_bytes

# prometheus.yml
scrape_configs:
  - job_name: 'node'
    static_configs:
      - targets: ['localhost:9100']

# 告警规则
groups:
  - name: resource-alerts
    rules:
      - alert: HighCPU
        expr: 100 - (avg by(instance) (rate(node_cpu_seconds_total{mode="idle"}[5m])) * 100) > 80
        for: 5m
        labels:
          severity: warning
        annotations:
          summary: "CPU 使用率过高"
          description: "实例 {{ $labels.instance }} CPU 使用率 {{ $value }}%"
      
      - alert: HighMemory
        expr: (1 - (node_memory_MemAvailable_bytes / node_memory_MemTotal_bytes)) * 100 > 90
        for: 5m
        labels:
          severity: warning
        annotations:
          summary: "内存使用率过高"
          description: "实例 {{ $labels.instance }} 内存使用率 {{ $value }}%"
      
      - alert: MemoryLeakSuspected
        expr: (node_memory_MemAvailable_bytes - node_memory_MemFree_bytes) / node_memory_MemTotal_bytes > 0.01
        for: 30m
        labels:
          severity: info
        annotations:
          summary: "怀疑内存泄漏"
          description: "实例 {{ $labels.instance }} 内存持续增长"

6.2 Grafana 仪表板

// Grafana Dashboard JSON(简化版)
{
  "panels": [
    {
      "title": "CPU 使用率",
      "type": "graph",
      "targets": [
        {
          "expr": "100 - (avg by(instance) (rate(node_cpu_seconds_total{mode=\"idle\"}[5m])) * 100)",
          "legendFormat": "{{instance}}"
        }
      ]
    },
    {
      "title": "内存使用率",
      "type": "graph",
      "targets": [
        {
          "expr": "(1 - (node_memory_MemAvailable_bytes / node_memory_MemTotal_bytes)) * 100",
          "legendFormat": "{{instance}}"
        }
      ]
    },
    {
      "title": "进程内存 Top 10",
      "type": "table",
      "targets": [
        {
          "expr": "topk(10, process_resident_memory_bytes)",
          "format": "table"
        }
      ]
    }
  ]
}

七、总结

7.1 排查流程

CPU

内存

性能问题

CPU还是内存

top/htop 查看

free/vmstat 查看

定位进程

定位进程

pidstat/top -H

pmap/smaps

perf/py-spy

火焰图分析

定位代码问题

7.2 排查命令速查

命令 用途
top 实时 CPU/内存监控
htop 更友好的 top
vmstat 虚拟内存统计
pidstat 进程 CPU/内存统计
pmap 进程内存映射
perf top CPU 采样分析
valgrind 内存泄漏检测
py-spy Python 火焰图

7.3 常见问题解决

问题 原因 解决
CPU 单核 100% 死循环/GIL 优化算法/多进程
CPU 多核 100% 锁竞争 减少锁粒度
内存持续增长 泄漏/缓存无界 清理/有界缓存
内存周期性增长 GC 未触发 调整 GC 参数
进程 OOM 内存泄漏 修复代码

7.4 最佳实践

实践 说明
监控先行 使用 Prometheus/Grafana
火焰图分析 精准定位热点
有界缓存 使用 LRU/TTL
连接池 复用连接
定期 GC Python/Java 清理
资源清理 finally/with 语句

本文基于常见性能问题编写。如有问题欢迎评论区讨论!

Logo

openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构

更多推荐