The Runtime Theory
hardApplicationDSA#threads#os#concurrency#scheduling

What happens when a thread is created?

Tests the OS-to-application boundary: syscall, kernel thread vs user thread, stack allocation, scheduling, and the real costs — locking, cache effects, and context switches.

The Runtime Theory Team2 min readasked at google · meta · amazon · cloudflare

This question tests whether you can trace a thread from a language call to the scheduler — and whether you know where the real costs are. The interviewer wants the syscall boundary, the stack, and the scheduler; the trap is thinking threads are cheap.

The walkthrough. When you call thread::spawn or new Thread(...):

  1. The runtime asks the OS via a syscall (clone on Linux, pthread_create underneath) to create a kernel-visible execution entity. This is a transition from user mode to kernel mode, not a tiny operation: the kernel allocates a task structure, assigns a thread ID, and inserts the thread into the run queue.
  2. A stack is allocated. A new thread needs its own stack — on Linux, typically 8 MB of virtual address space (RSS grows on touch). The stack is mapped with guard pages at the bottom so overflow faults cleanly.
  3. Thread state is initialized: registers, instruction pointer pointing at the thread entry function, thread-local storage block (TLS), signal mask, and the thread's scheduling properties (policy, priority, cgroup/CPU affinity).
  4. The thread becomes runnable. From here the scheduler decides when it actually runs — it may sit in the run queue while the current CPU is busy. The spawn call returns before the new thread necessarily executes anything.

The real costs — this is the part people miss:

  • Creation is expensive: tens of microseconds to ~milliseconds depending on OS and allocator state, because it involves syscalls, kernel structures, and an mmap for the stack.
  • Memory commitment: each thread reserves stack and TLS; thousands of threads reserve gigabytes of virtual memory, which can exhaust address space even when physical usage is low — and every touched page becomes RSS.
  • Context switches: switching threads means a kernel mode round trip plus TLB flushes and cache misses — the threads' shared L1/L2 state doesn't transfer.
  • Lock and cache contention: threads share the heap, so allocating concurrently means atomic operations, and false sharing of adjacent fields in one cache line becomes a real slowdown.

Tradeoffs and edge cases worth naming:

  • User threads (goroutines, Java virtual threads) sidestep this: the runtime multiplexes many cheap logical threads onto a few kernel threads, so spawn is a heap allocation plus a queue push, not a syscall — but any blocking syscall in a user thread can block its carrier kernel thread.
  • Thread pools exist because spawn/teardown is expensive; you reuse carriers.

A strong closing: "Creating a thread is a syscall, a stack, and a scheduler entry — not a heap object. It's expensive enough that pools exist, and cheap enough that a well-designed pool is the answer to 'how many threads'."

This answer walks

Follow-ups they'll push on

  1. 01What's the difference between a kernel thread and a user thread (or goroutine)?
  2. 02How much memory does a thread stack reserve, and why is it virtual?
  3. 03Why is creating thousands of threads bad but thousands of goroutines fine?
  4. 04What happens on a context switch?

More interviews in this topic

One dispatch a week

The trace behind each question, the tradeoff that explains it, and one technical dispatch per week — no noise.

One technical dispatch per week. No noise.