The GVL Explained
Why threads are limited.
The GVL Explained is a free Ruby Academy lesson on CoddyKit — lesson 2 of 4. You can read the complete lesson below for free — then practise it hands-on in the browser with a built-in code editor and a 24/7 AI tutor. It is part of the Ruby Academy learning path, one of 4 lessons in the course, and your progress syncs across the web and the CoddyKit app.
What Is the GVL?
The GVL (Global VM Lock, historically the GIL) is a lock inside CRuby, the standard Ruby interpreter.
- It allows only one thread to execute Ruby code at a time.
- It protects the interpreter's internals from concurrent corruption.
- It means pure-Ruby CPU work does not truly run in parallel on multiple cores.
Understanding the GVL explains when threads help and when they do not.
Why It Exists
CRuby's internals (object allocation, garbage collection, C extensions) were not all written to be thread-safe. The GVL makes them safe by serializing Ruby execution.
Removing it cleanly is hard, so CRuby keeps it. JRuby and TruffleRuby do not have a GVL and can run threads on many cores.
CPU-Bound Work
For CPU-bound tasks (math, sorting, parsing) threads do not speed things up under CRuby. Only one thread runs Ruby code at any instant, so two CPU threads roughly equal one thread plus overhead.
def heavy
sum = 0
1_000_00.times { |i| sum += i }
sum
end
puts heavy
puts "CPU work does not parallelize under the GVL"I/O Releases the GVL
The key insight: blocking I/O releases the GVL. When a thread sleeps, reads a file, or waits on a socket, it lets go of the lock so another thread can run Ruby.
This is why threads are great for I/O-bound work even with the GVL.
Overlapping Waits
Here three threads each sleep. Because sleep releases the GVL, the waits overlap. The total time is about 0.2s, not 0.6s.
start = Time.now
threads = (1..3).map do
Thread.new { sleep 0.2 }
end
threads.each(&:join)
elapsed = Time.now - start
puts "Elapsed roughly: " + elapsed.round(1).to_sC Extensions Can Release It
Well-written C extensions can release the GVL during long native computations using rb_thread_call_without_gvl. This lets heavy native work run in parallel while Ruby threads continue.
So some libraries achieve real parallelism even on CRuby.
Threads Still Switch
Even for CPU work the GVL does not freeze other threads forever. CRuby preempts a thread periodically so each gets a turn. The work is concurrent (interleaved) but not parallel (simultaneous).
results = []
mutex = Mutex.new
threads = (1..3).map do |n|
Thread.new do
mutex.synchronize { results << n }
end
end
threads.each(&:join)
puts "Collected: " + results.sort.join(", ")Concurrency vs Parallelism
Two important words:
- Concurrency: tasks make progress by interleaving. The GVL allows this.
- Parallelism: tasks run at the exact same instant on different cores. The GVL prevents this for Ruby code.
Threads give you concurrency; Ractors give you parallelism.
Getting Around the GVL
To use multiple cores for CPU work in CRuby you can:
- Run multiple processes (each has its own GVL).
- Use Ractors for in-process parallelism.
- Offload to C extensions that release the GVL.
For I/O work, plain threads are usually enough.
Processes Have No Shared GVL
Each OS process runs its own Ruby VM with its own GVL. Forking creates true parallelism but processes do not share memory and use more resources.
puts "Main pid: " + Process.pid.to_s
puts "Each forked process has its own independent GVL"Choosing a Strategy
Decision guide:
- Lots of waiting? Use threads.
- Heavy CPU, want cores? Use processes or Ractors.
- Need shared state? Threads with a
Mutex, but mind the GVL limits.
Knowing the GVL prevents the classic mistake of expecting CPU threads to scale linearly.
Quick Check
Test your understanding of the GVL.
Recap: The GVL
Key takeaways about the Global VM Lock:
- Only one thread runs Ruby code at a time in CRuby.
- It protects the interpreter's internals.
- Blocking I/O releases it, enabling overlapping waits.
- CPU-bound Ruby work does not parallelize across cores.
- Use processes or Ractors for true parallelism.
Next we look at Fibers for lightweight cooperative concurrency.
Frequently asked questions
Is the “The GVL Explained” lesson free?
Yes — the full text of “The GVL Explained” is free to read here on the web, and the Ruby Academy course includes 4 lessons in total. To practise it interactively (a built-in code editor and a 24/7 AI tutor) and unlock the rest of the Ruby Academy course, upgrade to CoddyKit PRO.
What will I learn in “The GVL Explained”?
Why threads are limited. You practise Ruby Academy with hands-on code you run directly in the browser, and a 24/7 AI tutor answers your questions as you work through the lesson.
Do I need any experience to start Ruby Academy?
No prior experience is required. Ruby Academy on CoddyKit is structured for beginners through advanced learners; this is — lesson 2 of 4, so you can start here or from the beginning and move at your own pace.
How long does the “The GVL Explained” lesson take?
Most CoddyKit lessons take about 5–10 minutes. Each one is bite-sized and interactive, so you make steady progress and pick up exactly where you left off across the web and the app.
Can I write and run code in this Ruby Academy lesson?
Yes. Every Ruby Academy lesson includes a built-in code editor, so you write and run real code right in your browser and get instant AI feedback — no local setup required.