JVM 内存调优实战:从 GC 日志分析到生产故障排查

JVM Memory Tuning in Practice: From GC Log Analysis to Production Troubleshooting

| iDev PR | 2026-08-28T09:19:49

JVM 内存调优是 Java 应用性能优化的核心环节。本文通过真实案例讲解如何分析 GC 日志、识别内存泄漏、选择合适的垃圾收集器以及制定调优策略。

JVM memory tuning is central to Java application performance optimization. This article explains GC log analysis, memory leak identification, garbage collector selection, and tuning strategies through real-world cases.

理解 JVM 内存模型JVM 内存分为堆内存(Heap)和非堆内存(Non-Heap)。堆内存是 GC 管理的主要区域,存放对象实例。非堆内存包括 Metaspace(存放类元数据)、直接内存(Direct Memory)和线程栈等。生产环境中的内存问题通常出现在堆内存区域。GC 日志分析开启 GC 日志是调优的第一步。JDK 17 使用统一日志框架(Unified Logging):-Xlog:gc*:file=gc.log:time,level,tags 开启详细 GC 日志关注 GC 暂停时间(Pause Time)和 GC 频率Young GC 频繁通常意味着年轻代空间不足Full GC 频繁可能预示内存泄漏或老年代空间不足垃圾收集器选择JDK 17 推荐使用 G1 或 ZGC。G1 是默认收集器,适合大多数场景,通过 -XX:MaxGCPauseMillis 设置目标暂停时间。ZGC 是低延迟收集器,GC 暂停时间通常在1毫秒以内,适合对延迟敏感的应用。对于内存小于4GB的应用,Serial 或 Parallel 收集器可能反而更高效。真实案例:内存泄漏排查某次生产环境告警显示 Old Gen 持续增长。我们首先通过 jmap -dump 导出堆快照,然后使用 Eclipse MAT 分析。发现是一个本地缓存(ConcurrentHashMap)没有设置过期策略,导致缓存对象持续增长。解决方案是替换为 Caffeine 缓存并设置合理的 TTL 和最大容量。这个案例说明,手动管理的缓存是内存泄漏的高发区。


Understanding JVM Memory ModelJVM memory is divided into heap memory and non-heap memory. Heap memory is the primary area managed by GC, storing object instances. Non-heap memory includes Metaspace (storing class metadata), Direct Memory, and thread stacks. Memory issues in production environments typically occur in the heap memory region.GC Log AnalysisEnabling GC logs is the first step in tuning. JDK 17 uses the Unified Logging framework:-Xlog:gc*:file=gc.log:time,level,tags enables detailed GC loggingFocus on GC pause time and GC frequencyFrequent Young GC typically indicates insufficient young generation spaceFrequent Full GC may signal memory leaks or insufficient old generation spaceGarbage Collector SelectionJDK 17 recommends G1 or ZGC. G1 is the default collector suitable for most scenarios, with target pause time set via -XX:MaxGCPauseMillis. ZGC is a low-latency collector with GC pause times typically under 1 millisecond, suitable for latency-sensitive applications. For applications with less than 4GB of memory, Serial or Parallel collectors may actually be more efficient.Real Case: Memory Leak InvestigationA production alert showed continuous Old Gen growth. We first exported a heap dump using jmap -dump, then analyzed it with Eclipse MAT. The root cause was a local cache (ConcurrentHashMap) without an expiration policy, causing cached objects to grow continuously. The solution was replacing it with Caffeine cache configured with reasonable TTL and maximum capacity. This case demonstrates that manually managed caches are a common source of memory leaks.

← Back to News