Spark Core · Module 1 of 5 · Overview
Who does what in a Spark application
A 10-minute refresher: the parts of a Spark application, the units of work they run, and how executor memory is divided. Use the deep-dive and production pages when you need more.
The three moving parts
A Spark application is one driver plus the executors it gets from a cluster manager. The driver plans the work and schedules tasks. Executors are JVM processes on worker nodes that run those tasks, one per core, and hold shuffle files and cached data. The cluster manager only hands out machines; it doesn't schedule tasks.
- The driver asks the cluster manager for executors (fixed count, or dynamic allocation).
- The cluster manager starts executor JVMs on worker nodes with the requested cores and memory.
- Executors register back with the driver. From now on the driver talks to them directly.
- For each stage, the task scheduler sends tasks (your serialised code plus one partition to work on) to free cores, preferring executors that already hold that data.
- Executors report status and send small results back. Shuffle files and cached data stay on the executors.
Jobs, stages and tasks
An action creates a job, shuffles split the job into stages, and each stage runs one task per partition on executor cores. The driver does all the planning; executors only ever see tasks.
Application one SparkSession: driver + executors
└─ Job one per action (count, write, collect) ← created by the driver
└─ Stage a new one at every shuffle ← cut by the DAG scheduler
└─ Task one per partition, one core each ← run by executors
Module 3 walks a real pipeline through this: Jobs, stages and tasks →
Executor memory at a glance
Think of an executor as a shared office floor. The cluster manager rents you the whole floor (the container). Spark carves it into fixed areas, and one area, the unified pool, is shared by every task running on that executor's cores. Example: spark.executor.memory=16g, 4 cores, defaults.
Container (what YARN / Kubernetes allocates) 17.6 GB
├─ Overhead, off-heap: JVM internals, network, Python 1.6 GB max(384 MB, 10% of heap)
└─ JVM heap = spark.executor.memory 16 GB
├─ Reserved for Spark internals 0.3 GB fixed
├─ User memory: your objects, UDF data structures 6.3 GB 40% of the rest
└─ Unified pool 9.4 GB 60% of the rest
├─ Storage: cache, broadcast variables ~4.7 GB ┐ soft boundary,
└─ Execution: shuffle, join, sort, aggregate ~4.7 GB ┘ can move
- Execution can evict cache (down to the protected half), but cache can't evict execution. Losing cache only costs a recompute; losing a running join's memory would break the task.
- Memory per task ≈ pool ÷ cores. Here about 2.35 GB per task with no cache, 1.2 GB if cache holds its half. Going past that means spill (slow) or OutOfMemoryError.
- "Java heap space" means the heap ran out. "Container killed … exceeding memory limits" means overhead ran out (often Python). Different causes, different fixes.
Key facts
| Fact | Rule or default |
|---|---|
| Slot | One executor core runs one task at a time. Total slots = executors × cores. |
| Waves | Tasks ÷ slots. Stage time ≈ waves × task time, and never less than the slowest task. |
| Deploy mode | Client: driver on the submitting machine (interactive). Cluster: driver inside the cluster (production). |
| Reserved memory | 300 MB, fixed |
spark.memory.fraction | 0.6 of (heap − 300 MB) forms the unified pool |
spark.memory.storageFraction | 0.5 of the pool is protected for cache |
spark.executor.memoryOverhead | max(384 MB, 10% of heap), outside the heap |
| Memory per task | ≈ unified pool ÷ cores per executor |
| Dynamic allocation | Adds executors when tasks wait over 1 s; removes them after 60 s idle. Needs a shuffle service or shuffle tracking. |
| Speculation | Off by default. Re-runs a task slower than 1.5 × median once 75% of the stage is done. Doesn't fix skew. |
Say it in an interview
Quick check
Does the cluster manager schedule tasks?
10 executors × 4 cores, a stage of 1,000 tasks taking 20 s each. How long?
"Container killed by YARN for exceeding memory limits." Heap or overhead?
spark.executor.memoryOverhead or reduce Python memory use. Full scenario →A 16 GB, 4-core executor spills heavily. Two fixes that don't add memory?
Does speculative execution fix data skew?
Go deeper
Wave timeline, the memory layout drawn to scale with a calculator, how executors are added and removed, how stragglers get a backup copy, and deploy modes.
Production issuesExecutor memory: failures and fixesEight common memory failures, each with the real error, how to confirm it, the fix, prevention and an interview answer. Plus a mitigation cheat sheet.