Base CS from zero
The runtime
You write a function in TypeScript. You call it. It runs. You allocate an array. The array appears. You throw an error. It propagates up through callers. You never thought about what memory address the array lives at, or what happens to it when you no longer need it, or how the error knows which function to return to.
All of that is taken care of by something that is always running alongside your program, invisibly. That something is called the runtime (or the runtime system or the runtime environment).
The runtime is not your code. You did not write it. It was written by the language implementors and shipped as part of the language installation. But it is running all the time your program is running, doing essential work that your code depends on. Understanding the runtime is understanding what is actually below the surface of every program you have ever run — including the programs you write yourself.
After this lesson you can define what a runtime system is, name the four main services runtimes provide (memory management, call-stack machinery, standard library, and the interpreter or VM itself), explain what garbage collection is and why it is needed, and describe how your code sits on top of the runtime.
What a runtime is. A runtime system (often just called the runtime) is the code and machinery that a language implementation installs alongside your compiled or interpreted program to handle the services that the CPU and OS alone do not provide.
When you install Python, Node.js, or the Java Development Kit, you are not just installing a compiler or interpreter — you are installing a runtime. When your program starts, the runtime starts too, often before your first line of code runs. The runtime stays active for the entire life of the process, and it performs essential services that your code delegates to it silently.
Think of it this way: the CPU provides raw execution (fetch, decode, execute). The OS provides processes, files, and hardware access. The runtime sits between the language and the OS, providing the higher-level services the language’s semantics promise: automatic memory management, safe calling conventions, built-in data structures, and so on.
Even compiled languages have runtimes. A C program compiled to a binary still links
against the C standard library (libc), which provides printf, malloc, free,
memcpy, and hundreds of other functions. The C runtime also handles program startup
(setting up the stack, calling main) and program exit (flushing buffers, calling
registered cleanup functions). The runtime is thinner in C than in Python, but it is
always there.
Service 1: memory management and garbage collection. Every time your program creates a new object, a new array, or a new string, it needs a chunk of memory to store it. On a raw machine, obtaining memory means calling an OS service to get a block of addresses and tracking those addresses yourself. Most high-level languages hide this: the runtime manages a region of memory called the heap and hands out pieces of it when your code creates new values.
In languages like C, you manage memory manually: you call malloc to allocate a block and
free to release it when you are done. If you forget to free, that memory is wasted for
the life of the process — a memory leak. If you free memory you are still using, you
get a crash or a security vulnerability — a use-after-free.
Most modern languages (Java, Python, JavaScript, Go, C#) avoid this by providing automatic memory management through a component called the garbage collector (GC). The garbage collector is a part of the runtime that periodically scans the heap to find all objects your program can no longer reach (objects with no remaining references from any live variable). It then frees those objects automatically. Your code just creates objects; the GC cleans up behind you.
The GC is not free. It runs its own machine code periodically, pausing or running concurrently with your program. This adds latency and consumes some CPU time, which is one reason carefully tuned C or Rust code can outperform Python or JavaScript on CPU-intensive tasks.
Service 2: call-stack machinery. Every function call needs to track its local variables and where to return after the function finishes. The call stack is the memory region where this information is stored. Each function call pushes a stack frame onto the call stack; the frame holds the function’s local variables, the return address (where to resume execution after the function returns), and some housekeeping data. When the function returns, its frame is popped off the stack.
The call-stack machinery — the rules for how frames are laid out in memory, how arguments are passed between functions, how the return address is saved and restored — is part of the runtime’s responsibilities. For most compiled languages, this is defined by the platform’s calling convention (a standard agreed upon by the OS and compiler). For interpreted languages and virtual machines, the runtime manages a virtual call stack in software.
You rely on the call stack every time you call a function, even if you never think about it. When a runtime error says “stack overflow” (because you wrote infinite recursion), it means the call stack grew so deep that it ran out of the memory the runtime allocated for it.
Service 3: the standard library. A standard library is the collection of functions
and data structures that comes packaged with the language and are available without
installing any extra package. Examples: Python’s len(), range(), open(), and the
json module. JavaScript’s Array.prototype.map(), Math.sqrt(), and Date. Java’s
java.util.ArrayList and java.io.File.
The standard library is part of the runtime. When your Python program calls len(my_list),
it calls a function whose code lives in the Python runtime’s binary, not in your program.
The runtime ships the code; your code calls it.
Standard libraries typically include: data structures (lists, dictionaries, sets), string operations, mathematical functions, file and network I/O, date and time handling, concurrency primitives, and formatting/parsing utilities. The quality and breadth of a language’s standard library is a major factor in programmer productivity — Python’s “batteries included” philosophy (rich standard library) is one reason it is popular for scripting and data work.
Service 4: the interpreter or virtual machine. If the language is interpreted or compiles to bytecode, the runtime also includes the engine that actually runs your code. For Python, this is the CPython interpreter. For Java and Kotlin, it is the JVM. For C# and F#, it is the .NET Common Language Runtime (CLR). For JavaScript running in a browser or Node.js, it is the V8, SpiderMonkey, or JavaScriptCore engine.
These engines are themselves compiled binaries — programs written in C or C++ and compiled ahead-of-time for the host platform. They run your code by interpreting it or compiling it to native code (via JIT). The engine is as much a part of the runtime as the garbage collector and the standard library.
When you run node server.js, you are running the Node.js binary (which includes V8, the
garbage collector, the Node.js standard library, and the event loop machinery). All of
that is the runtime. Your server.js file is the user code on top.
Why this works
Why can’t the OS just provide all these services? The OS provides a general-purpose process environment — raw memory, file handles, network sockets, threads. It does not know or care about your language’s type system, your object model, or how your language defines scope and closures. The runtime bridges the OS’s general-purpose interface and your language’s specific semantics. A Java runtime knows about Java objects, Java types, and the Java memory model. The OS knows only about pages of memory and system calls. The runtime is the adapter that makes the OS’s raw services look like the language’s high-level promises.
Edge cases
Languages with almost no runtime. C and Rust are often called “systems languages”
because they have very thin runtimes. A Rust binary compiled with #![no_std] has
essentially no runtime at all — no GC, no standard library, no exception handling
machinery. The programmer manages memory manually (in Rust, via ownership rules enforced
by the compiler rather than a runtime GC). This makes such programs suitable for
microcontrollers and OS kernels, where there is no underlying OS or runtime to rely on,
but it shifts responsibility back to the programmer.
Tracing the runtime services involved in one TypeScript line.
Consider this line in a Node.js program:
const users = JSON.parse(fs.readFileSync("users.json", "utf8"));This one line engages at least four runtime services simultaneously:
-
Standard library —
fs.readFileSync: Your code calls the Node.js standard library functionreadFileSync. The runtime makes an OS system call to open the file, read its bytes into a memory buffer, and return the buffer. You never wrote the OS system call; the runtime did. -
Standard library —
JSON.parse: Your code calls the JavaScript built-inJSON.parse. The runtime runs the JSON parsing code (which lives in V8’s C++ source, not your TypeScript). It allocates intermediate objects on the heap during parsing. -
Heap / garbage collector: The parsed result — an object with fields, possibly nested arrays — is allocated on the heap by the runtime. You never called
malloc. The runtime found a block of heap memory and placed the object there. Whenusersgoes out of scope, the GC will eventually free that memory. -
Call-stack machinery:
readFileSyncandJSON.parseare both function calls. The runtime pushed and popped stack frames for each, managing the return addresses and local variables. The runtime also handles the case where an exception is thrown insidereadFileSync(file not found), unwinding the call stack to propagate the error to your code’s nearest try/catch block.
None of this appears in your one line of TypeScript. The runtime does it all.
A garbage collector automatically frees memory that the program can no longer reach. In languages without a GC (like C), who is responsible for freeing memory? Type 1 for 'the programmer (manually)' or 2 for 'the OS (automatically)'.
The call stack stores stack frames, one per active function call. Each frame holds the function's local variables and a return address. What does the return address tell the CPU? Type 1 for 'where to resume after the function returns' or 2 for 'the function's name as a string'.
When you call 'JSON.parse(text)' in JavaScript, which piece of software contains the actual code that parses the JSON? Type 1 for 'your JavaScript file' or 2 for 'the JavaScript runtime (V8 or similar)'.
A program runs 'infinite recursion' — each call immediately calls itself again. Which runtime service fails and causes a crash? Type 1 for 'the garbage collector' or 2 for 'the call stack'.
Even a compiled C program has a runtime. The C runtime performs two tasks at startup before main() is called. Type 1 if 'setting up the stack and initialising the process' is one of those tasks, or 0 if it is not.
What is the runtime, and how does your code relate to it?
A runtime system (the runtime) is the code and machinery that the language implementation provides and runs alongside your program. It supplies four major services: (1) memory management — allocating heap space for new objects and, in most modern languages, a garbage collector that automatically frees memory your code no longer references; (2) call-stack machinery — stack frames, return addresses, and calling conventions that make function calls and returns work correctly; (3) the standard library — the collection of built-in functions and data structures (I/O, collections, math, strings) that ship with the language; and (4) the interpreter or virtual machine itself, for interpreted or bytecode languages. Your code sits on top of all these layers. Even compiled languages have runtimes (the C runtime handles startup, the standard library, and stack setup). The richer the runtime, the more work it does on your behalf — at the cost of some CPU time and memory for runtime bookkeeping.