java heap space 解决方法,How to increase Java heap size in JVM

题图来自Unsplash,基于CC0协议
导读
Java Heap Space 错误,或者说 java.lang.OutOfMemoryError: Java heap space,是 Java 应用程序中最常见的内存错误之一。它通常表示程序运行时请求创建新的 Java 对象失败,因为请求的内存量已经超出了虚拟机分配给应用程序的堆内存最大限制。解决这个问题需要从正确诊断原因入手,然后采取针对性措施。
一、 了解 Heap Space 错误
当 JVM 尝试分配一个新的 Java 对象,而堆内存中没有足够的连续空间来容纳这个对象时,就会抛出此错误。这通常发生在以下情况:
- 堆内存不足: 应用程序本身就可能需要非常大的堆来存储状态和数据。
- 内存泄漏: 应用程序存在内存泄漏,导致对象没有被垃圾回收器(GC)回收,随着时间推移,一直持有的对象占用的堆空间越来越多,最终导致堆无法容纳更多的新对象。
- 大量大对象被频繁创建: 程序不断地实例化非常大的对象(例如,大的图像、大量大文本记录等),即使分代大小配置得当,也可能很快填满老年代或整个堆。
二、 诊断 Heap Space 错误
发生错误时,通常程序会崩溃。为了找出根本原因,需要进行一些诊断:
- 错误堆栈信息: Java 会指向 try-catch 块或未处理的异常创建点,但不会直接说明是内存问题。
- 启用 GC 日志: 这是最重要的诊断工具。可以通过 JVM 启动参数(如
-XX:+PrintGCDetails -XX:+PrintGCDateStamps -Xlog:gc*)打开详细的 GC 日志。这可以帮助观察对象的分配模式、年轻代/老年代的垃圾回收行为、元空间使用情况等,判断分配失败是否是直接原因,以及虚拟机是如何回收内存的。 - 分析 Heap Dump: 当 JVM 由于
OutOfMemoryError崩溃时,通常可以配置自动生成 Heap Dump 文件(例如-XX:+HeapDumpOnOutOfMemoryError参数)。使用工具如 Eclipse MAT、VisualVM、JDK Mission Control 或 JProfiler 分析 Heap Dump 文件,可以找出哪些是占用大量内存的对象,是否存在不必要的对象持有、循环引用、未使用的臃肿对象等,从而定位潜在的内存泄漏点。 - 监控应用性能: 在生产环境使用能监测 JVM 与应用性能的监控工具(如 Prometheus/Grafana + Micrometer, New Relic, Dynatrace 等),观察 Heap 水位的变化,可以提前预知潜在的 Heap Space 问题。
三、 常用解决方案:调整 JVM 参数
如果确认是堆内存不足或分配策略不恰当,可以通过调整 JVM 参数来解决:
(1) 增加 Heap Size (增大内存分配)
这是最直接也最常用的方法,尤其是针对堆内存不足的情况:
-
基本参数:
-Xmx<Size>: 设置堆内存最大可用空间,例如-Xmx2g表示最大堆内存为 2GB。注意:不应设置超过操作系统和系统可用内存的限制,否则系统稳定性受影响。-Xms<Size>: 设置堆内存初始大小,例如-Xms512m。通常建议将-Xms和-Xmx设置为相同,以避免 JVM 在运行时反复调整堆大小带来的性能开销。
-
更精细控制堆结构:
-XX:NewSize和-XX:MaxNewSize: 分别设置年轻代(New Generation)的最小和最大可分配大小。调整年轻代大小会影响 Minor GC 的频率和效率。过大可能导致后续老年代空间不足,过小可能导致 Minor GC 频繁但处理能力不足。-XX:MaxMetaspaceSize: 设置元空间(Metaspace)的大小限制(JDK 8 及以上替换-PermSize)。元空间用于存储类的元数据,也可能耗尽可用内存。
(2) 垃圾回收器选择与调优
不同的垃圾回收器有不同的特性和最适用场景。选择适合的应用场景和进行相应的调优可以显著提升 JVM 管理堆的能力,减少 OutOfMemoryError 的发生:
- G1 Garbage Collector (默认于 JDK 9+ for server模式): 设计目标是获得可预测的吞吐量,兼顾吞吐量和延迟。它将堆划分为多个区域(Region),并优先回收含有最多垃圾的区域。
- Parallel Scavenge (年轻代) 与 Old Generation: Parallel Scavenge 是年轻代的垃圾收集器,并搭配 Parallel Old (老年代)。目标是最大化吞吐量,适用于 CPU 密集型应用,但也关注暂停时间。
- CMS (Concurrent Mark-Sweep) Garbage Collector (老年代): 主要用于注重响应时间的应用场景,其特点是尽可能与用户线程并行执行(仅标记和清除,不进行整理),以压缩老年代 GC 的暂停时间。但 CMS 有其缺点,如对浮动垃圾容忍,可能导致 Full GC,未来已被 Shenandoah 和 ZGC 替代大部分场景的应用。
- Zul Garbage Collector (ZGC): 设计目标是实现纳秒级暂停时间,适用于非常大堆内存(TB 级别)的场景,能将 GC 暂停时间限制在非常低的水平。
- Shenandoah GC: 类似 ZGC,旨在实现短 GC 暂停时间,它并行执行垃圾回收,但努力将所有活动对象(不仅仅是老年代)的移动和重新安置与用户线程收尾。也被设计用于大堆内存场景。
大抵调:-XX:+UseG1GC, -XX:+UseParallelGC, -XX:+UseConcMarkSweepGC, -XX:+UseZli GC, -XX:+UseShenandoahGC。
四、 垃圾回收调优参数
除了选择具体的 GC,还有一些相关的调优参数:
-XX:MaxGCPauseMillis<millis>: G1 收集器的目标,尝试每次 GC 后暂停时间不超过指定值。值越低,GC 次数可能越多,总时间可能不降反升,需要实际测试验证。-XX:GCDrainLoop(G1): 增加 GCDrainLoop 配置参数有助于提高 STW 暂停时间的稳定性。-XX:CMSPrecleanEnabled(CMS): 启用软参考处理或偏置清除以提前完成 CMS 初始标记和 Remark 阶段,以便在并发标记期间更早开始 concurrent 可用性扫描。-XX:ReduceRefinedObjectsDuringCDSO: 该参数可以减少应用程序启动时(对于 CDS-O 的 app)保留对象的数量。
五、 常见特定场景:Java Heap Space 在 Tomcat 中的问题
Tomcat 作为常用的 Java Web 容器,其运行也需要 JVM 的支持并共享堆内存。解决 Tomcat 中的 java.lang.OutOfMemoryError: Java heap space:
- 检查 Tomcat JVM 内存配置: Tomcat 的启动脚本(如
catalina.sh)中通常有JAVA_OPTS或CATALINA_OPTS环境变量。设置-Xmx和-Xms参数来增加分配给 Tomcat 应用程序的堆大小。 - 检查 Tomcat 线程池和连接池: 过多的线程或未正确配置的连接池(如数据库连接池)可能导致资源耗尽,间接引起内存问题。调整
server.xml中的maxThreads参数以及连接池(如 Tomcat JDBC Pool 或 BoneCP)的最大连接数。 - 分析特定 Web 应用内存泄漏: 如果是某个 webapp 导致的问题,可能需要单独分析该应用的 Heap Dump。可以尝试将有问题的应用停掉再启动看看问题是否重现。
- 检查是否有大量大对象被创建并缓存: 这是内存泄漏的典型原因。
六、 预防措施与最佳实践
- 避免不当的优化(-Xmn): 手动调整年轻代大小要特别小心,不合适的参数设置反而可能导致性能下降。
- 代码层面:
- 避免在循环或长时间运行的方法中创建不必要的大对象。
- 使用弱引用(WeakReference)、软引用(SoftReference) 和 虚引用(PhantomReference) 来管理可能被大量创建且容易陈旧化的对象,如缓存、资源引用等。
- 及时清除不再需要的对象引用,帮助 GC 及时回收。
- 审查设计避免内存雪球效应(例如,服务处理请求时消息被放入队列,处理速度跟不上放入速度,全部对象占满内存导致 JVM 无法分配新对象)。
- 定期进行内存分析: 使用 Heap Dump 工具定期分析生产环境的应用 Heap,建立基线,以便提前发现问题。
- 监控与预警: 关注 Heap Memory Usage、GC 暂停时间等指标,设定阈值告警。
- 压力测试: 对应用进行负载测试,模拟高峰并发和数据量,观察是否在特定负载下发生 Heap Space 错误。
- 升级 JDK 和库: 某些 Heap Space 问题可能由已知的虚拟机或库 bugs 引起,使用更新的版本可能会修复这些问题。
解决 Java Heap Space 问题是一个结合诊断、调整和预防的过程。从明确错误原因开始,通过调整 JVM 配置、优化代码设计、进行事件分析并辅以监控,才能有效应对和最终根除 OutOfMemoryError: Java heap space 错误。
© 版权声明
本文由盾科技原创,版权归 盾科技所有,未经允许禁止任何形式的转载。转载请联系candieraddenipc92@gmail.com