In Dart, how does JIT compilation on the Dart VM differ from AOT compilation, and when do you use each?
answer
- compile while running versus before shipping
- incremental recompilation enables hot reload
- consistent, short startup
- runtime still ships: GC and type checks
- jit-snapshot trains on a real run
basics
~20 sJIT compiles Dart code while the VM runs it, which enables incremental recompilation, hot reload and rich debugging during development. AOT compiles to native machine code before shipping, giving consistent, short startup for production executables and apps.
solid answer
~50 sThe Dart VM's **JIT** compiler turns code into machine code while the program runs. That supports incremental recompilation - the basis of hot reload - plus DevTools metrics and full debugging, so `dart run` and development builds use it. The **AOT** compiler compiles the whole program to native ARM or x64 code ahead of time, tree-shaking what is unused; the result starts quickly and consistently and needs no compiler at runtime, which is why `dart compile exe` output and production app builds use it. AOT code still runs inside a small Dart runtime that provides the garbage collector, runtime type checks and isolates. AOT gives up hot reload and some features such as `dart:mirrors`. A middle option, `dart compile jit-snapshot`, stores code compiled during a training run so a JIT program starts faster, and can reach higher peak performance if the training data is good.
code
bash · 6 lines# JIT: run from source during development
dart run bin/my_tool.dart --verbose
# AOT: compile once, ship a self-contained executable
dart compile exe bin/my_tool.dart -o build/my_tool
./build/my_tool --verbosego deeper
Recall that JIT is used while developing with dart run and AOT for the shipped executable.
Explain startup, hot reload, tree shaking and the runtime that AOT code still carries, plus what jit-snapshot and kernel are.
Choose the output format for a deliverable by startup, portability, peak performance and library restrictions such as dart:mirrors.
Weigh distribution simplicity against platform coverage when deciding how a team ships Dart tools and services.
## Two compilers on the native platform On native targets - Windows, macOS, Linux, Android, iOS - Dart has two ways to turn source into machine code. - **Just-in-time (JIT)**: the Dart VM loads the program and compiles functions to machine code as they are executed, optimising hot paths based on what it observes. - **Ahead-of-time (AOT)**: a compiler translates the entire program to native machine code before it ever runs, and the result is shipped. | | JIT (Dart VM) | AOT | |---|---|---| | When code is compiled | while the program runs | before distribution | | Startup | slower; code is compiled on first use | consistent and short | | Hot reload | yes, via incremental recompilation | no | | Debugging and DevTools | full | limited | | Needs the SDK to run | yes (`dart run`) | no; a runtime is embedded or supplied | | Typical use | development, scripts | production executables and apps | ## What JIT buys you in development The dart.dev platform overview lists what the VM's JIT offers: **incremental recompilation**, which enables hot reload; **live metrics** that power DevTools; and **rich debugging**. `dart run` uses it, which is why you can run Dart code without compiling it first. The same property is why development builds of Flutter apps use the JIT - the build modes themselves are a Flutter topic. ## What AOT buys you in production AOT output launches with **consistent, short startup**, because no compilation happens at launch. The compiler also performs **tree shaking**, dropping code the program cannot reach. AOT code is not bare machine code with nothing around it: it runs inside the **Dart runtime**, which manages memory with a generational garbage collector, enforces the sound type system with the runtime checks that remain (such as `as` casts), and manages isolates. With `dart compile exe` that runtime is embedded in the executable; with `dart compile aot-snapshot` you supply it through `dartaotruntime`. The trade-offs of AOT: 1. **No hot reload** - you rebuild to see changes. 2. **No `dart:mirrors`**, and per the docs no `dart:developer`, in `exe` and `aot-snapshot` output. 3. **Platform-specific output** - an AOT file built on macOS runs on macOS; Linux is the only cross-compilation target. ## The in-between formats `dart compile` offers two formats that stay on the JIT side: - **`jit-snapshot`** - to build it, the compiler actually **runs your program** as a training run and saves the parsed classes and the code compiled during that run. When the VM starts from the snapshot, it skips parsing and compiling what was already seen, so user code starts sooner. The docs note JIT-compiled code can have **faster peak performance than AOT** code if the training data is good, because the JIT optimises using observed behaviour. It is architecture specific and runs with `dart run app.jit`. - **`kernel`** - a portable binary form of the program's abstract syntax tree, runnable with `dart run app.dill` on any OS and architecture; it starts faster than source but much slower than AOT output. ## Scenario: shipping a CLI For a command-line tool you distribute, AOT is the usual choice: users get a single file that starts instantly and does not need the Dart SDK installed. During development you keep using `dart run`, with its fast edit-run cycle and debugger. ## Common mistakes - Believing AOT output has no runtime at all, and so no garbage collector. - Expecting hot reload against an AOT build. - Assuming JIT is always slower; its peak performance can match or beat AOT on long runs. - Using `dart:mirrors` in code meant for an AOT executable.
- Does an AOT-compiled Dart executable still have a garbage collector?Yes. AOT code runs inside a small Dart runtime that manages memory with a generational garbage collector, enforces the runtime type checks that remain, and manages isolates. `dart compile exe` embeds that runtime; `aot-snapshot` output relies on `dartaotruntime` to provide it.
- What is surprising about building a jit-snapshot with dart compile?The compiler performs a training run: it actually executes your program and records the classes and code compiled along the way. Side effects of `main` happen at build time, and the snapshot is only as good as the paths that run exercised.
JIT is an interpreter who translates a speech sentence by sentence and gets faster as patterns repeat; AOT is a translated book printed before the event - instant to read, but changing a sentence means reprinting.
saying these in an interview costs you the question
- Thinks AOT-compiled Dart has no runtime or garbage collector
- Believes hot reload works against an AOT build
- Says JIT code is always slower than AOT code
- Assumes dart:mirrors works in dart compile exe output
- Thinks you must compile a Dart program before running it