Back to the map

Orientation · Lesson 2 of 2

From source code to a running process

What happens between writing code and having it serve a request: compiling, linking, the executable file, how the kernel loads it into a process with its own address space, and how interpreters and JIT compilers change the picture.

IntroMust-knowJunior 10 min Updated Nov 2026

Why it matters

"What happens when you run a program?" is the question that ties the whole track together, and it shows up in practice more than people expect. Cold starts, slow first requests after a deploy, a container that will not start because a shared library is missing, a process killed for using too much memory: each is a step in the journey from source to running process going wrong. Knowing the steps lets you place the problem instantly.

The idea

Source code is text. To run, it must become machine instructions, be packaged into a file the operating system understands, and be loaded into a process: a running program with its own memory, registers and resources. There are three main routes from source to instructions.

RouteWhen code becomes machine instructionsExamplesConsequence
Ahead-of-time compiledBefore deployment, by a compilerC, C++, Rust, GoFast start, fast steady state, one binary per platform
InterpretedNever fully; an interpreter executes the program line by line or from bytecodeCPython, Ruby (classic)Simple to ship, slower execution
Just-in-time compiledAt runtime, for the hot parts, by the runtime watching what runs oftenJava, C#, JavaScript (V8)Slow at first, very fast once warm

The compiled path, step by step

  1. 1

    Compile

    The compiler turns each source file into an object file of machine instructions, with placeholders for functions and data defined elsewhere.
  2. 2

    Link

    The linker resolves those placeholders. Static linking copies library code into the binary; dynamic linking leaves references to shared libraries (like libc) to be resolved when the program starts.
  3. 3

    Produce an executable

    The result is a file in a format the kernel understands (ELF on Linux, Mach-O on macOS, PE on Windows): instructions, initial data, and a header saying where to start.
  4. 4

    Start a process

    A shell or service manager asks the kernel to create a process (on Unix, fork to copy the current process, then exec to replace it with the new program). The kernel sets up a fresh address space and maps the executable's sections into it.
  5. 5

    Load and run

    For dynamically linked programs, the dynamic loader maps the shared libraries and fixes up references. Then execution jumps to the entry point, runtime initialisation runs, and finally your main function.

The process's address space

Every process sees its own private range of memory addresses, laid out in regions. The kernel and hardware translate these virtual addresses to physical memory, which the memory management module covers in depth.

Kernel spacenot accessible to the programStack (per thread)locals, return addresses ↓… free …Heapnew / malloc ↑Dataglobals, staticsTextmachine instructions, read-onlystack overflow: deep recursionout of memory: heap growthmapped from the executable
A process's virtual address space. Code and constants come from the executable; the heap grows up as you allocate; each thread's stack grows down as functions call functions.

Interpreters and JITs

An interpreted or JIT-compiled program is really the interpreter's or virtual machine's process running your code as data. The JVM, for example, starts, loads your classes as bytecode, interprets them, measures which methods are hot, and compiles those to optimised machine code in the background. That is why a Java or .NET service is slower for its first seconds or minutes after a deploy: it is literally still compiling itself. Mitigations are warming the service with synthetic traffic before it takes real load, ahead-of-time compilation (GraalVM native image, ReadyToRun), and class data sharing to speed up startup.

Trade-offs

Ahead-of-time compilation gives fast starts and predictable performance at the cost of per-platform builds. Interpretation gives portability and simplicity at the cost of speed. JIT gives excellent steady-state speed, sometimes beating static compilation because it optimises for the real workload, at the cost of warm-up time and memory. Static linking makes a binary self-contained and container-friendly; dynamic linking shares memory between processes and lets security patches to a library apply without rebuilding.

In an interview

Use it when cold starts or deploys come up: "The service is on the JVM, so new instances are slow until the JIT has compiled the hot paths; we warm each instance with replayed traffic before the load balancer marks it healthy." Or for containers: "We build a statically linked Go binary into a minimal image, so startup is milliseconds and there are no shared libraries to go missing."

Check yourself

After every deploy, a Java service's p99 latency is poor for about two minutes and then settles, with no change in traffic. What is the most likely cause?

From source code to a running process · Backplane