skip to content

What does the activation record pushed for one call to a telemetry ingest routine actually hold?

level: middleimportance: must knowfreq 60%

answer

  1. one call, one block of storage
  2. return address plus saved caller state
  3. parameters, locals, temporaries, result
  4. code is shared, the frame is not
  5. pushed on entry, released on return

basics

~20 s

An activation record is the block of storage created for one call. It holds the return address, saved caller state such as the previous frame's base, the incoming parameters, the routine's locals, temporaries for part-finished expressions, and room for the result.

solid answer

~40 s

An **activation record**, or frame, is the storage a single call needs while it is live. It holds the **return address** so control can resume where the call was made; **saved caller state**, typically the previous frame's base and whatever registers the routine must give back untouched; the **parameter slots** the call filled; the routine's **locals**; **temporaries** holding part-finished expressions; and space for the **result**. One call, one frame — that is why two calls of the same routine, including a routine calling itself, do not interfere: each has its own parameters and locals. The code is shared; the frame is not. Frames are pushed on entry and released on return in strict last-in-first-out order, which is what makes allocation a pointer bump and deallocation free.

code

pseudocode · 10 lines
pseudocode
// one frame, pushed for one call of ingest(reading, record)

[ return address      ]   // where to resume in the caller
[ saved frame base    ]   // how to find the caller's frame again
[ parameter: reading  ]   // filled by the call
[ parameter: record   ]
[ local: checksum     ]   // declared inside ingest, this call only
[ local: fieldCount   ]
[ temporary: t1       ]   // a part-finished expression
[ result space        ]   // where the returned value is placed

go deeper

for a junior

Recall the list: return address, saved caller state, parameters, locals, temporaries, result space. Know that one call means one frame, and that a routine's code is shared while its frame is not.

for a middle

Explain why per-call frames are what make a routine re-entrant, how the saved frame base restores the caller's view, and why last-in-first-out ordering makes allocation a pointer bump and release free.

for a senior

Use the frame to diagnose. Read a crash depth as frame size times depth against a bounded region, and recognise when a routine's wide locals, not its call count, are the reason a workload fails.

for a principal

Treat frame cost as a budget you set policy around: which call chains are allowed to be deep, where recursion is permitted at all, and how you keep a hot path's frames small enough to stay within the reservation you have chosen.

A routine's text is written once, but a *call* of it needs storage of its own: somewhere to remember what it was handed, what it has computed so far, and where to go when it finishes. That storage is the **activation record** — commonly called the **frame** — and it is created when the call begins and released when the call returns. Keep the setting concrete: a device driver calls a telemetry ingest routine once per sensor reading, handing it a record to fill. Every one of those calls gets its own frame. ## What is in the frame | Part | What it is for | |---|---| | Return address | The point in the caller to resume at when this call finishes | | Saved caller state | The caller's frame base, and registers this routine must hand back unchanged | | Parameter slots | The values (or locations) the call copied in | | Locals | The variables declared inside the routine, for this call only | | Temporaries | Storage for part-finished expressions and for arguments being assembled for a nested call | | Result space | Where the value being returned is placed, when it does not simply travel in a register | Two things are deliberately **not** in it. The routine's instructions are not: code is shared by every call, and copying it per call would be absurd. And anything whose lifetime must outlast the call is not: a frame is released at the return, so storage that has to survive lives elsewhere. ## Why one frame per call Each call needs its **own** parameters, locals and return address, because each call is at a different point in its work and was handed different arguments. That is the entire reason a routine can call itself, or be entered again from a nested call, without the two interfering: the name `count` inside the routine resolves, at run time, to the slot in *this* call's frame. - A routine that recurses to depth 40 has 40 live frames, each with a full set of locals. - A routine entered again from something it called has two live frames, and the inner one's writes to its locals are invisible to the outer one. - The **return address** differs between those frames even though the routine is the same, because each was called from a different place. ## The stack discipline Frames come and go in strict last-in-first-out order: the most recently entered call is always the next to finish. That fact is what makes the frame store a **stack**, and it buys two things: 1. **Allocation is a bump.** Entering a call moves one marker by the frame's size; returning moves it back. There is no search for free space and no bookkeeping of which blocks are in use. 2. **Release is free and exact.** At the return, the whole frame stops being live in one step — no scan, no per-variable cleanup accounting. The price of that bargain is that the storage is reclaimed *whether or not* anyone still refers to it. A frame's lifetime is decided by the call structure, not by who still holds a route into it. ## Frame size A frame's size is usually fixed for a given routine and known before the program runs: the compiler counts the parameters, locals and the worst-case temporaries and reserves that much. It is not universal across routines — a routine with many wide locals has a much larger frame than one with two scalars — and it need not be fixed even for one routine, where a language allows a local whose size depends on an argument. This matters far beyond bookkeeping: how deep a routine can recurse is decided by its frame size against the bounded region the stack occupies, not by a count of calls. ## What this buys you when reasoning Most questions about calls become mechanical once you can picture the frame: - "Why doesn't the recursive call clobber my loop counter?" Because the counter is a local, and each call has its own. - "How does the routine know where to go back to?" Because the return address was written into the frame at the call. - "Why is my routine's state gone on the next call?" Because locals live in a frame that was released. - "Why did this crash only on the big batch?" Because depth multiplied by frame size outgrew the region reserved for frames. Note that a frame holds only what one call needs *while it is running*. Storage whose lifetime must exceed the call — a record a caller keeps, a buffer handed onward — cannot live in it, which is the single most consequential fact about the whole arrangement.

  • Why can two live calls of the same routine hold different values in the same local variable name?
    Because the name is resolved to a slot in the frame of the call that is executing, not to one fixed piece of storage. Each call pushed its own frame, so there are two slots with the same name and independent contents. This is exactly what makes a routine re-entrant and lets it call itself.
  • What in the frame lets the callee restore the caller's view of the stack, and why is that needed?
    A saved copy of the caller's frame base, recorded at entry. The callee's own locals are addressed relative to its base; at the return it restores the saved one so the caller's locals resolve correctly again. Without it, the caller would go on addressing its variables relative to a base that no longer describes its frame.
  • Is a frame always the same size for a given routine?
    Usually yes — the compiler counts parameters, locals and worst-case temporaries ahead of time and reserves a fixed amount. The exception is a language that allows a local whose size depends on an argument; then the frame grows at entry by an amount known only at the call. Across different routines, sizes vary widely.

saying these in an interview costs you the question

  • Says the frame holds only the routine's local variables
  • Claims a routine remembers its caller without storing anything
  • Thinks the routine's instructions are copied into each frame
  • Assumes every frame in the program is the same size
  • Expects frames to be reclaimed later by a collector, not at return
  • Says temporaries need no space because expressions are free