You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

Spark应用遇Java堆内存溢出:如何查看请求与可用内存?

如何查看Spark OOM时的内存请求量与可用内存

针对你的Spark应用遭遇OutOfMemoryError的问题,以及想获取内存请求发生时的具体请求量和可用内存的需求,我整理了几个实用方案,同时结合你提供的GC日志做下分析:

1. 调整GC日志参数,输出更细粒度的内存分配信息

你当前的GC日志只展示了GC前后的堆内存变化,但没有记录触发OOM的具体内存分配请求细节。可以给Spark的JVM添加以下参数,让日志输出关键的分配失败场景数据:

  • -XX:+PrintHeapAtGC:每次GC前后打印完整的堆内存布局,清晰展示各内存区域的已用/可用空间
  • -XX:+PrintAllocationFailure(部分JDK版本支持):专门打印分配失败时的内存请求大小,这正是你需要的核心信息
  • -XX:+PrintGCDetails:增强GC日志的细节,配合上面的参数使用效果更好
  • -XX:+PrintAdaptiveSizePolicy:如果你的JVM用了自适应堆大小策略,这个参数会输出堆区域调整的细节,帮助排查内存动态分配问题

给Spark应用添加这些参数的方式很简单,在提交命令里配置即可:

spark-submit --conf "spark.driver.extraJavaOptions=-XX:+PrintHeapAtGC -XX:+PrintGCDetails -XX:+PrintAllocationFailure" \
             --conf "spark.executor.extraJavaOptions=-XX:+PrintHeapAtGC -XX:+PrintGCDetails -XX:+PrintAllocationFailure" \
             your-application.jar

2. 用内存分析工具追溯分配请求细节

如果已经触发了OOM,还可以通过工具来回溯问题:

  • 生成堆转储并分析:先给JVM加上-XX:+HeapDumpOnOutOfMemoryError参数,下次OOM时会自动生成堆转储文件(.hprof),然后用Eclipse MAT或VisualVM打开,就能找到占用内存最大的对象,以及它们的分配调用栈
  • jmap实时查看:在应用运行时,用jmap -heap <进程ID>可以快速查看当前堆的整体使用情况,包括各代的已用/最大内存,能帮你实时掌握内存剩余
  • AsyncProfiler跟踪分配:这个工具可以采样内存分配请求,记录每个请求的大小和调用栈,精准定位是哪段代码在分配大内存

3. 从你现有GC日志中提取关键内存状态信息

先解读下你提供的日志里的内存情况,以第一条Full GC日志为例:

3429.458: [Full GC (Allocation Failure) [PSYoungGen: 3495918K->3495908K(6990848K)] [ParOldGen: 20969872K->20969870K(20971520K)] 24465790K->24465778K(27962368K), [Metaspace: 56727K->56723K(1101824K)] , 0.4597426 secs]

日志格式是[内存区域: GC前已用内存->GC后已用内存(区域总大小)],所以我们可以算出GC前的可用内存:

  • 年轻代(PSYoungGen)总大小6990848K,GC前已用3495918K,可用约6990848 - 3495918 = 3494930K(约3.3G)
  • 老年代(ParOldGen)总大小20971520K,GC前已用20969872K,可用仅20971520 - 20969872 = 1648K(约1.6M)
  • 整个堆总大小27962368K,GC前已用24465790K,可用约27962368 - 24465790 = 3496578K(约3.3G)

这次Full GC是因为Allocation Failure(分配失败),说明当时有一个内存请求在年轻代找不到足够空间,尝试晋升到老年代时,老年代几乎已经满了(仅剩1.6M),所以触发了Full GC,但GC后还是没有足够空间,最终导致了后续的OOM。另外,日志里的os::fork_and_exec failed: Cannot allocate memory (12)说明执行OOM的kill命令时,系统层面都没有可用内存了,这意味着你的应用不仅堆内存不足,连系统物理内存或swap都被耗尽了。

额外小建议

  • 先检查Spark的内存配置:spark.driver.memoryspark.executor.memory是否设置合理,别忘了堆外内存的配置(spark.executor.memoryOverhead),很多时候OOM是堆外内存不足导致的
  • 如果老年代内存持续增长,要排查是否有内存泄漏:比如未释放的缓存、过大的广播变量、RDD持久化策略不当(比如用了MEMORY_ONLY但数据量太大)等

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

火山引擎 最新活动