Spark 性能优化 (二):内存模型

在大数据计算和Java(包括Spark)中,**堆内存(On-Heap Memory)堆外内存(Off-Heap Memory)**是两个重要的概念,主要涉及内存管理、GC(垃圾回收)开销以及性能优化。下面从原理、区别、使用场景等方面进行详细解读。

1. 堆内存(On-Heap Memory)

定义

堆内存指的是 JVM(Java Virtual Machine)管理的内存空间,它由Java进程启动时分配,并受JVM的垃圾回收(GC)机制管理。在Spark、Flink等大数据计算框架中,默认情况下数据对象会存储在堆内存中。

特点

  • 由JVM管理,受GC控制。

  • 受JVM的-Xms(最小堆大小)和-Xmx(最大堆大小)参数限制。例如:

    复制代码
    java -Xms4G -Xmx8G -jar app.jar

    这里设置最小4GB,最大8GB的堆内存。

  • GC可能会影响性能:当堆内存数据过多时,GC操作会变得频繁,从而影响程序运行效率。

  • 内存访问速度相对较快。

**使用场景:**适用于存放生命周期较短的对象,例如

  • Java中的对象(List、Map等)
  • Spark RDD的缓存(默认使用堆内存)
  • 运行时计算所需的临时对象

2. 堆外内存(Off-Heap Memory)

定义

堆外内存是不受JVM管理的内存 ,即直接向操作系统申请的内存 ,通常是通过sun.misc.UnsafeByteBuffer.allocateDirect()进行分配。

特点

  • JVM无法直接管理,不会受GC影响,因此可以减少GC带来的延迟,提高性能。
  • 需要手动释放 (否则可能导致内存泄漏),通常使用sun.misc.UnsafeDirectByteBuffer进行管理。
  • 访问速度可能略低于堆内存,但由于减少了GC影响,在大数据场景下通常更高效。
  • 可以突破JVM的堆大小限制(如-Xmx设置的上限)。

使用场景:大数据计算(如Spark、Flink)

  • Spark的Tungsten优化(基于Unsafe的序列化)会使用堆外内存来存储数据,减少GC压力,提高计算效率。
  • Spark 2.x开始支持堆外存储,可以通过以下参数控制:
java 复制代码
spark.conf.set("spark.memory.offHeap.enabled", true) 
spark.conf.set("spark.memory.offHeap.size", "2g") // 设置2GB的堆外内存

3. 堆内存 vs 堆外内存

对比项 堆内存(On-Heap Memory) 堆外内存(Off-Heap Memory)
管理方式 JVM管理,由GC回收 由操作系统管理,需手动释放
访问速度 访问速度快 略慢(但减少了GC的影响)
GC影响 受GC影响,可能会导致长时间停顿 不受GC影响,适用于高性能计算
适用场景 适用于小规模数据、短生命周期对象 适用于大规模数据处理,如Spark/Flink
内存上限 -Xmx限制 可以突破JVM的限制

4. Spark 内存区域划分

每个Executor的JVM堆内存(-Xmx)主要可以分为以下几个部分:

内存区域 说明
Reserved Memory 预留内存,防止OOM(默认300MB)
User Memory 用户内存(执行任务中的变量、数据结构等)
Spark Unified Memory Spark的统一内存管理(存储+计算)
Storage Memory 用于缓存RDD、DataFrame等数据
Execution Memory 用于Shuffle、Join、Sort等计算
  1. Reserved Memory(预留内存)

    • Spark默认会预留 300MB 内存,它被用来存储各种 Spark 内部对象,例如存储系统中的 BlockManager、DiskBlockManager 等等。
    • 这个值可以通过 spark.testing.memory 进行调整(不建议修改)。
  2. User Memory(用户内存)

    • 存放用户代码中的数据结构、对象等。
    • 例如 collect()toLocalIterator() 可能会消耗大量此类内存。
    • 占用总Executor内存的约 20-30%,但未做严格限制。
  3. Unified Memory(统一内存)

    • Spark 2.x 之后引入,Storage MemoryExecution Memory 共享内存,提高利用率。
    • Spark会动态调整 Storage MemoryExecution Memory 之间的比例,优先满足计算需求。
  4. Storage Memory(存储内存)

    • 用于缓存 RDD、DataFrame、广播变量等数据。
    • 当 Execution Memory 不够时,可能会被回收(动态分配)。
    • spark.memory.storageFraction 控制默认比例(默认 0.5)。
  5. Execution Memory(计算内存)

    • 用于Shuffle、Sort、Aggregation等操作的临时数据存储。
    • spark.memory.fraction 控制分配给 Storage + Execution 的内存比例(默认 0.6)。
    • 如果 Execution 需要更多空间,Storage 可能会被回收。

4.1 Storage Memory 和 Execution Memory 抢占规则

  • 如果对方的内存空间有空闲,双方就都可以抢占;
  • 对于 RDD 缓存任务抢占的执行内存,当执行任务有内存需要时,RDD 缓存任务必须立即归还抢占的内存,涉及的 RDD 缓存数据要么落盘、要么清除;
  • 对于分布式计算任务抢占的 Storage Memory 内存空间,即便 RDD 缓存任务有收回内存的需要,也要等到任务执行完毕才能释放。

5. 内存控制推荐策略

Spark的存储和计算内存默认是堆内存,但可以启用堆外内存优化:

复制代码
spark.conf.set("spark.memory.offHeap.enabled", true)  // 启用堆外内存
spark.conf.set("spark.memory.offHeap.size", "4g")    // 设定4GB堆外内存
  • 如果数据量较小(<10GB),建议使用堆内存(默认)。
  • 如果数据量较大,建议开启堆外内存,减少GC影响,提高计算效率。
数据规模 推荐存储方式
< 10GB 堆内存
10GB - 100GB 适量使用堆外内存
> 100GB 主要使用堆外内存

合理配置堆内存和堆外内存可以避免OOM(OutOfMemoryError)并提高计算性能

相关推荐
砸吧砸吧1 小时前
#mapreduce打包#maven:could not resolve dependencies for project
java·大数据·maven·mapreduce
乐闻x2 小时前
性能优化:javascript 如何检测并处理页面卡顿
前端·javascript·性能优化
别惊鹊3 小时前
hadoop集群配置-xsync脚本同步环境变量
大数据·linux·hadoop
计算机毕设定制辅导-无忧学长4 小时前
RocketMQ 性能优化与调优策略(一)
性能优化·rocketmq
太阳吖4 小时前
学习笔记之车票搜索为什么用Redis而不是ES?
大数据·elasticsearch·搜索引擎
mozun20205 小时前
我国新基建七大领域发展概况2025.3.13
大数据·5g·ai·新能源·工业互联网·新基建
风象南5 小时前
告别“我觉得”!用 JMH 搞懂你的 Java 代码性能
java·后端·性能优化
weixin_307779136 小时前
稳定运行的以Oracle数据库为数据源和目标的ETL性能变差时提高性能方法和步骤
数据库·oracle·性能优化·etl
樟小叶_公众号同名6 小时前
大数据学习(一):HDFS
大数据·hdfs
科技壹周谈6 小时前
「为爱发电」的硬核打开方式,涂鸦智能用AIoT引领智慧能源变革
大数据·人工智能·能源