13.7场景6 内存泄漏

分类: 故障排查 Troubleshooting 实战

场景 6:内存泄漏

欢迎回到第 13 章的学习。在上一节,我们学习了微服务调用失败的排查方法。现在我们要学习场景 6:内存泄漏。

本节将学习:内存 Metrics 异常、分析内存使用 Trace、查看内存相关 Logs、识别泄漏源头、以及内存优化。

内存 Metrics 异常

内存 Metrics 异常的作用是什么? 通过 Metrics 发现内存使用异常,确认内存泄漏问题。

如何查看内存 Metrics? 查看以下指标:

  • 内存使用率
  • 内存增长趋势
  • GC 频率和时间
  • 堆内存使用情况

异常指标示例:

  • 内存使用率持续上升
  • GC 频率增加但效果不明显
  • 堆内存使用接近上限

内存 Metrics 查询:

# 内存使用率
100 * (1 - (node_memory_MemAvailable_bytes / node_memory_MemTotal_bytes))

# JVM 堆内存使用率
jvm_memory_used_bytes{area="heap"} / jvm_memory_max_bytes{area="heap"} * 100

# GC 频率
rate(jvm_gc_collection_seconds_count[5m])

# GC 时间
rate(jvm_gc_collection_seconds_sum[5m])

分析内存使用 Trace

分析内存使用 Trace 的作用是什么? 使用 Trace 分析内存使用的模式,识别内存泄漏的触发点。

如何分析内存使用 Trace? 查看内存相关的 Trace,分析:

  • 内存分配的时机
  • 内存释放的时机
  • 内存使用模式

内存使用模式分析:

查看内存相关 Logs

查看内存相关 Logs 的作用是什么? 通过 Logs 分析内存相关的详细信息,找到内存泄漏的原因。

如何查看内存相关 Logs? 使用 LogQL 查询内存相关日志:

LogQL 查询示例:

# 查询 GC 日志
{level="info"} && {message=~".*GC.*"}

# 查询 OOM 错误日志
{level="error"} && {message=~".*OutOfMemoryError.*"}

# 查询内存警告日志
{level="warn"} && {message=~".*memory.*"}

识别泄漏源头

识别泄漏源头的作用是什么? 找到内存泄漏的具体源头,定位问题代码。

如何识别泄漏源头? 分析内存使用模式,识别:

  • 未释放的资源
  • 循环引用
  • 缓存未清理
  • 监听器未移除

常见内存泄漏原因:

// 1. Unclosed resources
public void processFile() {
    FileInputStream fis = new FileInputStream("file.txt");
    // Forgot to turn off fis
}

// 2. Cyclic references
class A {
    B b;
}
class B {
    A a;  // Cyclic references
}

// 3. The cache has not been cleaned
private Map<String, Object> cache = new HashMap<>();
// The cache continues to grow and has not been cleaned

// 4. The listener has not been removed
eventBus.addListener(listener);
// Forgot to remove the listener

内存优化

内存优化的作用是什么? 根据分析结果,实施内存优化方案,解决内存泄漏问题。

优化方案包括哪些呢?

第一个:修复内存泄漏。 修复代码中的内存泄漏问题。

第二个:优化内存使用。 优化内存使用模式,减少内存占用。

第三个:调整 GC 参数。 调整垃圾回收参数,优化 GC 性能。

第四个:增加内存限制。 增加应用内存限制,暂时缓解问题。

内存优化示例:

// Fix: Use try-with-resources
public void processFile() {
    try (FileInputStream fis = new FileInputStream("file.txt")) {
        // Using file streams
    } // Auto shut off
}

// Fix: Using weak references
private Map<String, WeakReference<Object>> cache = new HashMap<>();

// Fix: remove the listener
@Override
public void destroy() {
    eventBus.removeListener(listener);
}

// JVM GC Parameter optimization
-XX:+UseG1GC
-XX:MaxGCPauseMillis=200
-XX:G1HeapRegionSize=16m

本节小结

在本节中,我们学习了场景 6:内存泄漏:

第一个是内存 Metrics 异常。 通过 Metrics 发现内存使用异常,确认内存泄漏问题。

第二个是分析内存使用 Trace。 使用 Trace 分析内存使用的模式,识别内存泄漏的触发点。

第三个是查看内存相关 Logs。 通过 Logs 分析内存相关的详细信息,找到内存泄漏的原因。

第四个是识别泄漏源头。 找到内存泄漏的具体源头,定位问题代码。

第五个是内存优化。 根据分析结果,实施内存优化方案,解决内存泄漏问题。

故障排查流程: 内存 Metrics 异常 → 分析内存使用 Trace → 查看内存相关 Logs → 识别泄漏源头 → 实施内存优化 → 验证修复效果。

这就是场景 6:内存泄漏。通过场景 6 的学习,我们掌握了内存泄漏的排查方法。

在下一节,我们将学习场景 7:缓存命中率低。学习如何排查缓存问题。