This is a precision question. The interviewer wants the mechanism, not vibes: an interpreter pays dispatch per operation, a JIT compiles hot code into native machine code, and an AOT compiler has to make worst-case assumptions because it can't see runtime types.
Start with the interpreter. Every bytecode or AST node execution involves fetching an instruction, dispatching on its opcode (a switch, computed goto, or inline cache), and performing the operation — often with dynamic type checks. Even a loop body has hundreds of cycles of overhead per iteration even when the work is trivial. Correct but slow: it doesn't speculate, it just does the work every time.
A JIT sits between because it uses the interpreter's output — or direct profiling — to find hot paths, then compiles those specific instructions, often with runtime type assumptions baked in. The hot path becomes native code: no dispatch, no dynamic checks, values in registers. When the assumption fails (e.g., a variable starts holding a different type), the JIT deoptimizes back to the interpreter, throwing away the optimized frame. This is why tiered runtimes exist: interpreter (or a simple tier) first, then baseline compiled code, then optimizing tiers (like V8's Sparkplug and TurboFan, or JVM's C1 and C2). Waiting to compile means the optimizing compiler has profile data, so it can inline, devirtualize, and eliminate checks — things an AOT compiler can't do.
An AOT compiler compiles everything up front from static information only. No runtime profiling, no deoptimization, but also no speculative optimization: every virtual call must stay virtual, every type check must run. So for code that would benefit from type specialization, the JIT can win. The JIT is "slower than a compiler" in two senses: compile happens at runtime (warm-up latency, CPU cost during execution), and the JIT has to be conservative while it lacks profile data.
Edge cases worth naming:
- PGO lets an AOT compiler approximate JIT benefits at build time.
- Warm-up is a real cost — serverless cold starts and short-lived CLI processes never reach the optimizing tier.
- Deoptimization can make a hot loop slow twice: compile, run fast, hit an edge case, and fall back.
- Escaping analysis and scalar replacement are JIT-only wins — allocation elimination.
A strong closing: "The JIT isn't a third strategy; it's an interpreter that learns, compiling exactly the code the profiler says is worth it, and recompiling when reality contradicts it."