1.1 报错背景
本地ideal研发环境(windows)运行spark程序调试,报错如下:
21/03/29 12:28:36 ERROR SparkContext: Error initializing SparkContext.
java.lang.IllegalArgumentException: System memory 259522560 must be at least 471859200. Please increase heap size using the --driver-memory option or spark.driver.memory in Spark configuration.
1.2 原因分析
从报错内容上看SparkContext没有初始化成功,错误示内存资源不够,需要至少471859200
。
1.2.1 spark本地运行机制
Spark在本地运行(local)原理是使用线程模拟进程,所以整个集群启动在一个进程中。
1.2.2 源码分析
上源码(Spark 3.1.0,org.apache.spark.memory.UnifiedMemoryManager
):
object UnifiedMemoryManager {
// Set aside a fixed amount of memory for non-storage, non-execution purposes.
// This serves a function similar to `spark.memory.fraction`, but guarantees that we reserve
// sufficient memory for the system even for small heaps. E.g. if we have a 1GB JVM, then
// the memory used for execution and storage will be (1024 - 300) * 0.6 = 434MB by default.
private val RESERVED_SYSTEM_MEMORY_BYTES = 300 * 1024 * 1024
def apply(conf: SparkConf, numCores: Int): UnifiedMemoryManager = {
val maxMemory = getMaxMemory(conf)
new UnifiedMemoryManager(
conf,
maxHeapMemory = maxMemory,
onHeapStorageRegionSize =
(maxMemory * conf.get(config.MEMORY_STORAGE_FRACTION)).toLong,
numCores = numCores)
}
/**
* Return the total amount of memory shared between execution and storage, in bytes.
*/
private def getMaxMemory(conf: SparkConf): Long = {
val systemMemory = conf.get(TEST_MEMORY)
val reservedMemory = conf.getLong(TEST_RESERVED_MEMORY.key,
if (conf.contains(IS_TESTING)) 0 else RESERVED_SYSTEM_MEMORY_BYTES)
val minSystemMemory = (reservedMemory * 1.5).ceil.toLong
if (systemMemory < minSystemMemory) {
throw new IllegalArgumentException(s"System memory $systemMemory must " +
s"be at least $minSystemMemory. Please increase heap size using the --driver-memory " +
s"option or ${config.DRIVER_MEMORY.key} in Spark configuration.")
}
// SPARK-12759 Check executor memory to fail fast if memory is insufficient
if (conf.contains(config.EXECUTOR_MEMORY)) {
val executorMemory = conf.getSizeAsBytes(config.EXECUTOR_MEMORY.key)
if (executorMemory < minSystemMemory) {
throw new IllegalArgumentException(s"Executor memory $executorMemory must be at least " +
s"$minSystemMemory. Please increase executor memory using the " +
s"--executor-memory option or ${config.EXECUTOR_MEMORY.key} in Spark configuration.")
}
}
val usableMemory = systemMemory - reservedMemory
val memoryFraction = conf.get(config.MEMORY_FRACTION)
(usableMemory * memoryFraction).toLong
}
}
主要功能点:
systemMemory
变量定义了系统内存资源。
package org.apache.spark.internal.config
val TEST_MEMORY = ConfigBuilder("spark.testing.memory")
.version("1.6.0")
.longConf
.createWithDefault(Runtime.getRuntime.maxMemory)
其中默认值为Runtime.getRuntime.maxMemory
,这个值为java虚拟机(JVM)能够从操作系统获取的最大内存资源,如果启动虚拟机时候没有配置-Xmx
参数,那么就是256M=256*1024*1024 beytes
(机器物理内存大于1G)。
reservedMemory
变量为系统保留内存资源。优先使用TEST_RESERVED_MEMORY
的值,默认值是个表达式,如果IS_TESTING=True
(测试模式)则值为0,否则为:RESERVED_SYSTEM_MEMORY_BYTES=300M
。
val TEST_RESERVED_MEMORY = ConfigBuilder("spark.testing.reservedMemory")
.version("1.6.0")
.longConf
.createOptional
val IS_TESTING = ConfigBuilder("spark.testing")
.version("1.0.1")
.booleanConf
.createOptional
//定义
private val RESERVED_SYSTEM_MEMORY_BYTES = 300 * 1024 * 1024
minSystemMemory
变量为系统最小内存资源。定义为reservedMemory
的1.5倍。
从报错信息看,明显是触发下面的代码逻辑:
if (systemMemory < minSystemMemory) {
throw new IllegalArgumentException(s"System memory $systemMemory must " +
s"be at least $minSystemMemory. Please increase heap size using the --driver-memory " +
s"option or ${config.DRIVER_MEMORY.key} in Spark configuration.")
}
spark应用中未进行相关参数配置,reservedMemory
值为300M,那么minSystemMemory
值为450M,而应用程序为设置JVM参数,systemMemory
默认是256M。显然触发systemMemory < minSystemMemory
条件。
1.3 修复方法
根据上面的源码分析,我们有下面的修复方法。
1.3.1 生产环境
生产代码按照源码分析要求jvm虚拟机至少是450M以上的内存。
- 配置
-Xmx
参数,使得其远大于450M。
1.3.2 测试调试
- 配置
spark.testing.memory
参数
这时候systemMemory
=spark.testing.memory
参数的值,例如:
val sparkConf = new SparkConf()
.set("spark.testing.memory","2147480000")
//2147480000=2G
- 开启测试模式(
IS_TESTING=True
)
val sparkConf = new SparkConf()
.set("spark.testing","true")
这时候minSystemMemory
即为0。这时候异常条件不会触发。
- 指定
spark.testing.reservedMemory
参数的值(尽可能的小)
val sparkConf = new SparkConf()
.set("spark.testing.reservedMemory","0")
上面的配置下,minSystemMemory
的值也为0。
1.4 总结
Spark运行对内存资源进行了门槛限制,如果降低这个限制必须要特意显示配置测试相关的指标配置。