You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于Spring Boot的K8s部署Java应用内存未释放问题求助

Java Spring Boot微服务K8s环境下内存占用不释放问题排查

问题背景

  • 技术栈:Spring Boot 2.7.11 + OpenJDK 11,部署在Kubernetes集群
  • 无负载状态:Pod内存占用约470MB
  • 核心配置:
    • K8s资源:limits内存1200Mi、CPU1核;requests内存900Mi、CPU1核
    • JVM参数:-Xmx750M
  • 负载测试表现:每秒20次API请求,负载期间Pod内存升至约1024MB,测试结束后7-8小时内存仍未回落,且JVM堆内存峰值仅400-500M
  • 排查结果:用Eclipse MAT分析堆转储,未发现自研代码内存泄漏,仅JDK、Spring Boot框架类占用内存

核心疑问:负载结束后内存长期不释放,是K8s配置问题、JVM GC调优问题,还是两者不兼容导致?


可能原因及排查方向

1. 堆外内存未被回收

Pod的内存占用是整个Java进程的内存总和,堆内存只是其中一部分,剩余的堆外内存(直接内存、元空间、线程栈、JNI内存等)才是问题核心:

  • 直接内存:NIO组件、数据库驱动(如JDBC)常使用直接内存,默认大小等于-Xmx,负载时会分配大量直接内存,但GC不会主动回收,只有触发Full GC或内存不足时才会释放。可进入Pod执行jcmd <pid> VM.native_memory summary查看直接内存具体占用。
  • 元空间:用于存储类元数据,负载时可能生成大量动态类(如AOP代理、反射类),若这些类的加载器未被回收,元空间就不会释放。用jstat -gcmetacapacity <pid>可监控元空间使用变化。
  • 线程栈:负载时Tomcat线程池会扩容,默认保留核心线程数,每个线程栈默认占1M内存,这些空闲线程的栈内存会持续占用。检查server.tomcat.core-threads等配置,确认核心线程数是否过高。

2. JVM的内存保留机制

默认G1 GC在内存压力较小时,不会主动收缩堆内存,甚至堆外内存也不会主动释放——JVM倾向于保留已申请的内存,避免频繁与操作系统申请/释放内存带来的性能开销:

  • G1的堆收缩由-XX:MaxHeapFreeRatio(默认70)和-XX:MinHeapFreeRatio(默认40)控制,只有堆空闲比例超过70%时才会触发收缩。但堆外内存回收更被动,仅当JVM感知内存不足时才会处理。
  • 你的Pod内存仅用了1024M,远低于1200Mi的限制,JVM完全无内存压力,自然不会主动释放内存。

3. K8s资源配置的影响

你的requests内存设为900Mi,远高于无负载时的470MB:

  • requests是K8s调度时的资源预留,Pod启动后操作系统会为其预留至少900Mi内存,虽你显式设置了-Xmx750M,但JVM可能基于该预留值调整其他内存参数,间接导致内存占用偏高。
  • 内存限制1200Mi足够宽松,JVM没有回收内存的动力,若Pod内存接近限制,会触发激进GC释放内存。

解决建议

  1. 定位堆外内存占用:

    • 进入Pod执行jcmd <pid> VM.native_memory summary,找到堆外内存主要消耗项。
    • 显式配置直接内存上限:添加-XX:MaxDirectMemorySize=256M(根据业务实际调整),防止直接内存无限制增长。
    • 限制元空间大小:设置-XX:MaxMetaspaceSize=256M,避免元空间持续膨胀。
  2. 调整JVM GC参数:

    • 给G1 GC添加-XX:MaxHeapFreeRatio=50 -XX:MinHeapFreeRatio=30,降低堆空闲比例阈值,让JVM在空闲时主动收缩堆内存。
    • 添加-XX:+ExplicitGCInvokesConcurrent,确保System.gc()能触发并发GC(仅作应急,不推荐依赖),同时加上-XX:+HeapDumpOnOutOfMemoryError,方便后续排查OOM问题。
  3. 优化K8s资源配置:

    • 把requests内存降到更接近无负载时的实际值(如500Mi),减少操作系统内存预留,同时给JVM合理的内存压力。
    • 若业务允许,可设置内存阈值的存活探针,当内存占用过高时自动重启Pod,但这是兜底方案,优先从根源解决问题。
  4. 优化线程池配置:

    • 调整Tomcat核心线程数,比如设为server.tomcat.core-threads=20,避免负载后保留过多空闲线程占用内存。

内容的提问来源于stack exchange,提问作者Munendra Singh

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.08 11:35:30