JIT Compilation in .NET
The problem
Intermediate language (IL) isn't machine code, so it can't run directly on the CPU — something has to translate it, and doing that translation efficiently, without a slow startup delay or wasted work retranslating the same method repeatedly, is a real engineering problem with more than one reasonable answer.
Why now
The CLR and managed execution already established that .NET code runs as IL, not machine code, leaving the translation step unexplained; the JavaScript engine and JIT already covered one JIT strategy (interpret, then optimize hot functions) for a very different, dynamically-typed language — .NET's JIT solves the same translation problem for a statically-typed, compiled-first one, which changes what's worth optimizing and when.
Mental model
.NET's JIT compiles each method to native machine code the first time it's actually called, then reuses that compiled code for every subsequent call — unlike a purely interpreted start, there's no repeated re-interpretation, just a one-time compilation cost paid lazily, per method, exactly when it's needed. Tiered compilation refines this further: a quick, less-optimized first compilation gets methods running fast, with a slower, more optimized recompilation kicking in later for methods called often enough to be worth the extra investment — the same 'watch what's hot, then optimize it' instinct the JS engine's JIT already established, applied to a different starting point.
Requires
- The CLR & Managed Execution.NET Internals
JIT compilation is the specific mechanism the CLR uses to turn loaded IL into executable machine code; you need the CLR's load-and-execute model already in place before the translation step within it is meaningful.
Helps
- The JavaScript Engine & JIT CompilationBrowser Internals
JavaScript's JIT already established the general 'watch what's hot, optimize it' strategy for a dynamically-typed, interpret-first language; recognizing .NET's compiled-first, tiered JIT as the same instinct applied under different starting constraints makes it far more intuitive than learning it as an unrelated new idea.
Used in
- Every running .NET application's method execution, invisibly, on first call
- Tiered compilation and ReadyToRun images, both aimed at reducing JIT startup cost
- Explaining why a .NET application's first request can be slower than subsequent ones
- Ahead-of-time (AOT) compilation as an alternative to JIT for scenarios where startup latency matters most
Projects
- Benchmark a method's execution time on its very first call versus its 1000th call in a tight loop, and explain the difference in terms of JIT compilation and tiered recompilation
- Research and explain, in your own words, the tradeoff ahead-of-time (AOT) compilation makes against JIT — faster startup, but no runtime-adaptive optimization
Examples
- A method's very first invocation pays a one-time JIT compilation cost; every subsequent call reuses the already-compiled native code, which is why microbenchmarks that ignore 'warm-up' are misleading
- ASP.NET Core applications using ReadyToRun images ship some pre-JIT-compiled code specifically to reduce the cold-start latency of the first few requests after deployment
Resources
Mastery checklist
- I can explain when and how often a given method gets JIT-compiled
- I can explain tiered compilation and why a quick-then-optimized approach beats compiling everything fully upfront
- I can explain why a benchmark measuring only a method's first call is misleading
- I can explain the tradeoff AOT compilation makes compared to JIT