来自Spark配置文档 https://spark.apache.org/docs/latest/configuration.html#memory-management,我们了解以下有关spark.memory.fraction
配置参数:
用于执行和存储的(堆空间 - 300MB)的一部分。该值越低,溢出和缓存数据驱逐发生的频率就越高。此配置的目的是为内部元数据、用户数据结构以及稀疏、异常大的记录的情况下的不精确大小估计留出内存。建议将此值保留为默认值。
在撰写此问题时,此配置参数的默认值为 0.6。这意味着,对于具有 32GB 堆空间和默认配置的执行器,我们有:
-
300MB
保留空间(硬编码值this https://github.com/apache/spark/blob/0e2d604fd33c8236cfa8ae243eeaec42d3176a06/core/src/main/scala/org/apache/spark/memory/UnifiedMemoryManager.scala#L198 line)
-
(32GB - 300MB) * 0.6 = 19481MB
用于执行+存储的共享内存
-
(32GB - 300MB) * 0.4 = 12987MB
用户内存
这个“用户记忆”是(根据docs https://github.com/apache/spark/blob/master/docs/tuning.md#memory-management-overview)用于以下用途:
其余空间 (40%) 保留用于用户数据结构、Spark 中的内部元数据,以及在稀疏和异常大的记录情况下防止 OOM 错误。
在具有 32GB 堆空间的执行器上,我们为此分配 12.7GB 内存,这感觉相当大!
做这些用户数据结构/内部元数据/防止 OOM 错误真的需要那么大的空间吗?是否有一些引人注目的用户内存使用示例可以说明如此大的用户内存区域的需求?
我做了一些研究,我认为它的 0.6 不是为了确保用户内存有足够的内存,而是为了确保执行+存储可以适合 jvm 的旧代区域
在这里我发现了一些有趣的事情:火花调谐 https://spark.apache.org/docs/2.0.0/tuning.html
终身代大小由 JVM 的 NewRatio 控制
参数,默认为2,表示终身生成
新生代(堆的其余部分)大小的 2 倍。所以,通过
默认情况下,tenured Generation 占据 2/3 或大约 0.66
堆。 spark.memory.fraction 的值为 0.6 会保留存储空间并
老年代内的执行内存有空闲空间。如果
例如,spark.memory.fraction 增加到 0.8,那么 NewRatio 可能
必须增加到6个或更多。
因此,默认情况下,在 OpenJvm 中,该比率设置为 2,因此老一代有 0,66%,他们选择使用 0,6 来获得较小的余量
我发现在版本 1.6 中这被更改为 0,75 并且它导致了一些问题,这里是吉拉门票 https://issues.apache.org/jira/browse/SPARK-15796
在描述中,您将找到示例代码,该代码将记录添加到缓存只是为了使用为执行+存储保留的整个内存。将存储+执行设置为比旧一代更高的量时,gc 的开销确实很高,并且在旧版本上执行的代码(此设置等于 0.6)快了 6 倍(40-50 秒 vs 6 分钟)
经过讨论,社区决定在 Spark 2.0 中将其回滚到 0.6,这里是PR https://github.com/apache/spark/pull/13618/files有变化
我认为如果你想提高一点性能,你可以尝试将其更改为 0.66,但如果你想有更多的内存用于执行+存储,你还需要调整你的 jvm 并更改旧/新比率,否则你可能会面临性能问题
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)