Skip to content

Commit 1571877

Browse files
author
Andrew Or
committed
Carve out 300MB reserved memory
The new space used by storage and execution will be calculated by (JVM size - 300MB) * 75%, the `spark.memory.fraction`.
1 parent 60b541e commit 1571877

File tree

4 files changed

+36
-6
lines changed

4 files changed

+36
-6
lines changed

core/src/main/scala/org/apache/spark/memory/UnifiedMemoryManager.scala

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ import org.apache.spark.storage.{BlockStatus, BlockId}
4848
*/
4949
private[spark] class UnifiedMemoryManager private[memory] (
5050
conf: SparkConf,
51-
maxMemory: Long,
51+
val maxMemory: Long,
5252
private val storageRegionSize: Long,
5353
numCores: Int)
5454
extends MemoryManager(
@@ -130,6 +130,8 @@ private[spark] class UnifiedMemoryManager private[memory] (
130130

131131
object UnifiedMemoryManager {
132132

133+
private val RESERVED_SYSTEM_MEMORY_BYTES = 300 * 1024 * 1024
134+
133135
def apply(conf: SparkConf, numCores: Int): UnifiedMemoryManager = {
134136
val maxMemory = getMaxMemory(conf)
135137
new UnifiedMemoryManager(
@@ -144,8 +146,16 @@ object UnifiedMemoryManager {
144146
* Return the total amount of memory shared between execution and storage, in bytes.
145147
*/
146148
private def getMaxMemory(conf: SparkConf): Long = {
147-
val systemMaxMemory = conf.getLong("spark.testing.memory", Runtime.getRuntime.maxMemory)
149+
val systemMemory = conf.getLong("spark.testing.memory", Runtime.getRuntime.maxMemory)
150+
val reservedMemory = conf.getLong("spark.testing.reservedMemory",
151+
if (conf.contains("spark.testing")) 0 else RESERVED_SYSTEM_MEMORY_BYTES)
152+
val minSystemMemory = reservedMemory * 1.5
153+
if (systemMemory < minSystemMemory) {
154+
throw new IllegalArgumentException(s"System memory $systemMemory must " +
155+
s"be at least $minSystemMemory. Please use a larger heap size.")
156+
}
157+
val usableMemory = systemMemory - reservedMemory
148158
val memoryFraction = conf.getDouble("spark.memory.fraction", 0.75)
149-
(systemMaxMemory * memoryFraction).toLong
159+
(usableMemory * memoryFraction).toLong
150160
}
151161
}

core/src/test/scala/org/apache/spark/memory/UnifiedMemoryManagerSuite.scala

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -182,4 +182,24 @@ class UnifiedMemoryManagerSuite extends MemoryManagerSuite with PrivateMethodTes
182182
assertEnsureFreeSpaceCalled(ms, 850L)
183183
}
184184

185+
test("small heap") {
186+
val systemMemory = 1024 * 1024
187+
val reservedMemory = 300 * 1024
188+
val memoryFraction = 0.8
189+
val conf = new SparkConf()
190+
.set("spark.memory.fraction", memoryFraction.toString)
191+
.set("spark.testing.memory", systemMemory.toString)
192+
.set("spark.testing.reservedMemory", reservedMemory.toString)
193+
val mm = UnifiedMemoryManager(conf, numCores = 1)
194+
val expectedMaxMemory = ((systemMemory - reservedMemory) * memoryFraction).toLong
195+
assert(mm.maxMemory === expectedMaxMemory)
196+
197+
// Try using a system memory that's too small
198+
val conf2 = conf.clone().set("spark.testing.memory", (reservedMemory / 2).toString)
199+
val exception = intercept[IllegalArgumentException] {
200+
UnifiedMemoryManager(conf2, numCores = 1)
201+
}
202+
assert(exception.getMessage.contains("larger heap size"))
203+
}
204+
185205
}

docs/configuration.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -719,8 +719,8 @@ Apart from these, the following properties are also available, and may be useful
719719
<td><code>spark.memory.fraction</code></td>
720720
<td>0.75</td>
721721
<td>
722-
Fraction of the heap space used for execution and storage. The lower this is, the more
723-
frequently spills and cached data eviction occur. The purpose of this config is to set
722+
Fraction of (heap space - 300MB) used for execution and storage. The lower this is, the
723+
more frequently spills and cached data eviction occur. The purpose of this config is to set
724724
aside memory for internal metadata, user data structures, and imprecise size estimation
725725
in the case of sparse, unusually large records. Leaving this at the default value is
726726
recommended. For more detail, see <a href="tuning.html#memory-management-overview">

docs/tuning.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,7 @@ variety of workloads without requiring user expertise of how memory is divided i
114114
Although there are two relevant configurations, the typical user should not need to adjust them
115115
as the default values are applicable to most workloads:
116116

117-
* `spark.memory.fraction` expresses the size of `M` as a fraction of the total JVM heap space
117+
* `spark.memory.fraction` expresses the size of `M` as a fraction of the (JVM heap space - 300MB)
118118
(default 0.75). The rest of the space (25%) is reserved for user data structures, internal
119119
metadata in Spark, and safeguarding against OOM errors in the case of sparse and unusually
120120
large records.

0 commit comments

Comments
 (0)