<p><a href="https://en.wikipedia.org/wiki/Just-in-time_compilation" rel="noreferrer">Just-in-time compilation</a> (JIT) is a way of implementing a language where the source code is given as input (like with a typical interpreter), but rather than being directly interpreted, (some or all of) the code is compiled. This is done, for example, in the Python interpreter PyPy, and in most browsers' JavaScript implementations.</p>
<p>What are the advantages and disadvantages to JIT compilation, over typical interpreters, or compilation ahead of time? When would I choose JIT over something else?</p>

<design-choice-comparison><just-in-time>
What are the advantages and disadvantages of just-in-time compilation?
CC BY-SA 4.0
2023-05-22T23:54:20.070Z
This is not the usual definition of just-in-time compilation, which the Wikipedia page you linked to does have.
Michael Homer
CC BY-SA 4.0
2023-05-23T04:24:14.377Z
Without reading the wiki article, my understanding is that JIT typically involves compiling individual functions or even branches as-needed, rather than compiling the whole program upfront within the interpreter.
Bbrk24
CC BY-SA 4.0
2023-05-23T05:58:57.410Z
Note most browsers require a function to be run around ~1000 times before it will be considered for JIT, until then it's interpreted normally
mousetail 'he-him'
CC BY-SA 4.0
2023-05-23T09:38:45.083Z
Java uses a JIT compiler without having the source code available at runtime. You could argue that Java bytecode is the source code that the JIT compiles, but "source code" usually means the form in which the software is written or edited.
kaya3
CC BY-SA 4.0
2023-09-21T02:11:22.717Z
Hi, are the existing answers lacking? I don't want to write a new answer if any of them are acceptable (none are yet accepted); yet, my answer is going to be short: "Sometimes JIT is faster, sometimes slower; profile for your use case, and read [VM Startup Blows Hot & Cold](https://arxiv.org/abs/1602.00602v6). There are *no* known hard rules."
Corbin
<p>In certain cases having a JIT-compiler can be beneficial even in compiled code. Think of an image filter where you parametrize your kernel function on some runtime variables. If you compile this function just in time you have a lot more information available and thus more optimization opportunities than if you had compiled it ahead of time. For a function that is called millions of times in a hot loop this can really make a difference in terms of performance.</p>
<p><a href="https://github.com/jmmartinez/easy-just-in-time" rel="noreferrer">There is a library</a> that aims to bring jit-compilation to C++ for this purpose.</p>

CC BY-SA 4.0
2023-05-23T06:31:11.910Z
2023-05-27T13:24:21.383Z
2023-05-27T13:24:21.383Z
chrysante
CC BY-SA 4.0
2023-08-23T19:37:56.063Z
Do you mean that a specific version of the function/kernel is compiled for a specific _runtime_ value of some parameter(s)? So it'd eventually need to be recompiled for other values?
Pablo H
CC BY-SA 4.0
2023-08-29T17:26:30.287Z
@PabloH Yes, exactly
chrysante
CC BY-SA 4.0
2023-10-18T14:14:00.240Z
But even if runtime state is _not_ taken into account, JITted code can be compiled for the exact platform (processor, etc.) and so can still be faster than a compiled program which has to run on a wide variety of platforms.
gidds
<p>There is really only one advantage: Speed. Once compiled, JIT-ed code should run just as fast as native code. While there are many disadvantages the speed bonus is often worth it.</p>
<p>You can also incrementally apply optimizations for functions that get called a lot to reduce startup time. This can be done with real-world data about in what way the function gets called which could potentially allow more efficient optimization than when done ahead of time.</p>
<p>Some disadvantages include:</p>
<ul>
<li>Requiring taming of a dynamic type system. If everything can be any type the machine code will need to be littered with branches and not be very fast. Thus JIT-ed code will make assumptions about the types of objects based on watching them over time. However, if the types ever do not match the performance benefit is lost.</li>
<li>Security vulnerabilities. Despite being made by major companies with teams of highly skilled professionals all browsers have had many security issues related to their JavaScript JIT compiler. Mostly related to making assumptions when optimizing but not checking them properly.
<ul>
<li><a href="https://www.youtube.com/watch?v=45wMEIIPsPA" rel="nofollow noreferrer">This is a great video on the topic and why it's so hard to secure</a></li>
</ul>
</li>
<li>More memory overhead. You need to keep both the original code and JITed versions of many potential type combinations of each function in memory as well as needing both primitive low level and high level versions of objects, as well as constantly needing to keep them in sync.</li>
<li>Some types of events can basically invalidate some or all JITed code, requiring a slow recompile. This includes things like:
<ul>
<li>Changing the type of a global variable</li>
<li>Monkey patching some type</li>
<li>Self-modification</li>
</ul>
</li>
<li>Debugging can be harder. Typically debugging is done by just disabling the JIT portion but this would exclude bugs introduced by the JIT compilation itself.</li>
</ul>
<p>Generally you only JIT specific functions if they are run a lot with the same argument types and the same context, so if this never happens you have no downsides except the book-keeping to keep track of how often each function is run. You can JIT in a separate thread so as not to slow down the program while compiling and just interpret normally until then.</p>
<p>If your language is less dynamic many of the downsides might not apply and implementation might be more straightforward.</p>

CC BY-SA 4.0
2023-05-23T05:58:09.137Z
2024-04-23T21:11:35.043Z
2024-04-23T21:11:35.043Z
mousetail 'he-him'
CC BY-SA 4.0
2023-09-22T16:05:31.663Z
Another advantage is the ability to handle source languages where a program would specify an *infinite* number of specialized functions, but only execute a finite subset that is based upon the data a program receives.  This kind of pattern can exists with generics in .NET languages like C#.  Ahead-of-time generation of machine code would be impossible because nothing in the universe could know exactly what functions would be invoked until after execution had started.
supercat
CC BY-SA 4.0
2023-09-23T02:12:59.493Z
JIT-compiled code can actually be *faster* than AOT-compiled code, because the JIT compiler can treat things like function parameters as constants, where the AOT compiler needs to treat them as variables.  (This does mean the code needs to be recompiled when things change, so the compiler needs to be careful about where it's applied.)
Mark
CC BY-SA 4.0
2023-05-24T06:50:59.627Z
"Some types of events can basically invalidate all JITed code" — I don't immediately see why the events you list should invalidate _all_ JITed code and not a small part of it?
Alex Chichigin
CC BY-SA 4.0
2023-05-24T06:52:34.820Z
@AlexChichigin It can be really hard to prove in highly dynamic languages if a function depends on some global variable
mousetail 'he-him'
CC BY-SA 4.0
2023-05-24T07:10:28.587Z
In general (contrived) case — yes, but wast majority of cases are not general and don't use global variables at all, thus most of the code stays the same, and don't get invalidated. As far as I know...
Alex Chichigin
CC BY-SA 4.0
2023-05-24T09:36:07.367Z
The issue is, unless you can 100% prove that the global variable won't effect a function you need to regenerate it. Note the "global variable" can be something like `null`, `true`, or `object` which are also variables in Javascript
mousetail 'he-him'
<blockquote>
<p>JIT is a way of implementing a language where the source code is given as input</p>
</blockquote>
<p>Not necessarily at all. As we know both JVM and .NET JIT-compile byte-code and not the source code. The same is true for most WebAssembly runtimes.</p>
<p>As already mentioned, the main advantage of JIT over both interpretation and AOT is speed. For interpreters it's more evident as natively compiled code runs faster — almost always, there are exceptions though. Still JIT <em>can</em> outperform AOT compiled and optimized code due to availability of runtime information. That most pronounced for dynamically typed languages exactly because type (and object shape) information only fully emerges at runtime, but for statically typed languages too — as was also already mentioned, JIT can specialize (inline) and optimize algorithms for particular runtime parameters. Julia language does this with great success for many numerical algorithms.</p>
<p>The main user-facing disadvantage is start-up time. Again this is vividly exemplified by Julia language: before recent version 1.9 some libraries could take up to a couple of <em>minutes</em> to fully initialize and JIT, which was mitigated by employing (more) of AOT compilation at the library <em>install</em> time.</p>
<p>But most of other JIT-compilers including HotSpot JVM, .NET Core, V8, SpiderMonkey and others mitigate start-up latency problem via <em>tiered</em> compilation: they first run faster, less optimizing baseline JIT, and only after that a slower, better optimizing one.</p>

CC BY-SA 4.0
2023-05-24T07:07:31.130Z
2023-05-24T07:07:31.130Z
Alex Chichigin
CC BY-SA 4.0
2023-09-22T16:07:35.517Z
Another performance advantage of a JIT language like Java is that programs can benefit from improvements in a JIT compiler which take place after a program is published, even if such improvements exploit hardware features that didn't exist when the program was written.
supercat
CC BY-SA 4.0
2023-09-22T17:41:41.087Z
Nerd remark: a _language_ can't be JIT or not, it's an _implementation_ that can use JIT or not. :) Also, AOT-compiled programs in _source code form_ also benefit from compiler improvements after they were written. And one can argue a bytecode is closer to source code than machine code, so the lines are blurry...
Alex Chichigin
CC BY-SA 4.0
2023-09-22T17:48:29.763Z
Some languages, including Java, have historically been designed with the intention of facilitating JIT-based implementations.  Some go further and make it impossible to generate machine code for some constructs until after a program has started execution.  Compiling from source may be possible after publication, but in many the time required to compile a source-code program may exceed the time of a typical execution.  JIT-based languages are designed to make the JIT process fast.
supercat
CC BY-SA 4.0
2023-09-22T17:53:20.753Z
Historical note: Java _wasn't_ designed for JIT, it was only added in version 1.2. .NET was designed for JIT from the start, yep.
Alex Chichigin
<p><strong>It's complicated.</strong></p>
<h3>Security</h3>
<p>Compiling to native code is much less forgiving -- security-wise -- than interpreting. A stray read or write can very easily lead to an exploit.</p>
<p>As a result, compiling &quot;foreign&quot; code to native code on the fly -- whether via AOT or JIT compilation -- is fraught with peril. All mainstream JS engines have had CVEs filed against them, for example.</p>
<p>JIT (and AOT) compilers are therefore at a clear security disadvantage compared to interpreters when it comes to running &quot;foreign&quot; code.</p>
<h3>Size &amp; Complexity</h3>
<p>In general, a pure interpreter is (much) smaller than a JIT compiler, and thus much less complex. This ties in with the above security issue: the simpler, the easier to secure.</p>
<h3>Portability</h3>
<p>In general, a pure interpreter is more portable than a JIT compiler. A JIT compiler must be specialized for everything target platform (both architecture and OS), whereas a pure interpreter can rely on existing toolchains to be compiled for the right platform.</p>
<p>Conversely, a statically compiled binary is stand-alone and can be delivered on its own to any user, whereas for interpreted or JIT compiled language, the user typically must have both the &quot;program&quot; (in some form) and an interpreter or JIT compiler <em>of the right version</em>. Bundlers do exist, but the resulting applications are even bigger than statically compiled applications.</p>
<h3>Dynamic Languages</h3>
<p>So called dynamic languages -- ie, languages where the type of a value is not known at compile-time -- are a hard target for AOT compilers, so that in general there are only interpreters or JIT for them.</p>
<p>In such a situation, there's typically a start-up time vs run-time trade-off:</p>
<ul>
<li>An interpreter starts up near immediately but may run slower.</li>
<li>A JIT compiler starts up slower -- compiling everything it comes across -- but will run faster in the long term.</li>
</ul>
<p>The above trade-off can be alleviated (somewhat) by using tiered compilation, which is often used in JS engines, or the C# or Java runtimes. Tiered compilation is the idea of starting with the fastest start-up method, and over time to compile more and more optimized JIT code.</p>
<h3>Static Languages</h3>
<p>While in theory a JIT compiler should be able to outperform anything, in practice it rarely plays out and a JIT compiler typically ends up producing a program which runs slower<sup>1</sup> than one produced by an AOT compiler, due to a lower optimization budget.</p>
<p>There a specific exceptions: some workloads benefit from a run-time specialized inner loop.</p>
<p>The difference between theory and practice is perhaps not too surprising: in order for a JIT compiled program to outperform an AOT compiled program, it must take advantage of run-time information. This, in turn, means that a JIT <em>runtime</em> must not only JIT compile a program, but also <em>instrument</em> it to gather the necessary information, and <em>guard</em> it, in case the information it optimized for falls through.</p>
<p>This has 3 downsides:</p>
<ol>
<li>Information gathering takes up run-time, run-time that could be put to use to actually run the program, which the JIT compiler must then make up for, somehow.</li>
<li>Hot-patching -- used both for de-optimizing and splicing in a more optimized function -- requires to leave some &quot;scaffolding&quot; in place. Scaffolding which takes up run-time, and which once again the JIT compiler has to make up for, somehow.</li>
<li>Guards -- used for de-optimizing -- which are extra run-time checks, which the JIT compiler has to make up for, somehow.</li>
</ol>
<p>And of course, the very run-time used up to JIT compile also has to be made up for.</p>
<p>All in all, this means that JIT compiler start with a penalty<sup>2</sup>, compared to AOT compilers, and only if they uncover critical run-time information unlocking worthy optimizations will they able to produce a program which will outperform an AOT program.</p>
<p>And of course, very optimized AOT programs <em>also</em> use run-time information gathering for Profile Guided Optimizations, except ahead of time<sup>3</sup> so they don't pay for it at run-time.</p>
<p><sup>1</sup> <em>JIT compiled pre-optimized WASM produced by AOT compilers for C, C++, Rust, or Zig, for example, is routinely 2x slower than the natively compiled program. There's indubitably some overhead from the sandboxing applied, but still...</em></p>
<p><sup>2</sup> <em>A penalty on top of the hand tied behind their back, that is, as they typically have a much stricter &quot;optimization budget&quot; than AOT compilers, since they're doing the optimization work while the program is running...</em></p>
<p><sup>3</sup> <em>PGO ahead of time may suffer from artifacts effects, yes. But then again, the latest C# JIT will splice in a non-instrumented optimized version, so that should the workload change after optimization, it also uses the wrong profile. Why? Because keeping the instrumentation in would negatively affect performance...</em></p>

CC BY-SA 4.0
2023-09-22T12:51:24.607Z
2023-09-22T12:51:24.607Z
Matthieu M.
CC BY-SA 4.0
2023-09-22T16:11:27.023Z
I see no reason a JIT couldn't employ security-in-depth practices in much the same manner as interpreters.  A JIT may reap more benefit from identifying and eliminating seemingly-redundant operations, and thus many JIT compilers are more aggressive than interpreters in that regard, but a JIT which e.g. produces machine code that bounds-checks every array access could easily be less vulnerable to exploits than an interpreter which tries to identify and exploit places where checks can be omitted.
supercat
CC BY-SA 4.0
2023-09-22T18:38:34.133Z
JIT compilers introduce a new security problem, because JITs are *self-modifying code.* For security reasons, most modern OS use a $W \oplus X$ (write xor execute) discipline for memory locations. But a JIT generates machine code, meaning it needs to write (W) and then later jump to the written code (X). So the OS has to make memory that *may* have been compromised executable.
Martin Berger
CC BY-SA 4.0
2023-09-23T11:15:57.820Z
@supercat: A JIT is inherently more difficult. You can write an interpreter in a safe language, guaranteeing that no matter how much you mess up, you're still bound by the rules of the underlying language (and its runtime). A JIT generates assembly, the ultimate unsafe language. A single mistake in interpreting an untyped register, and all soundness flies out the window.
Matthieu M.
CC BY-SA 4.0
2023-09-25T15:18:52.280Z
@MatthieuM.: If code consists entirely of operational sequences which validate array indices, and maintain the invariant that anything that might hold an object reference and isn't null will always be a valid object reference, operations that would uphold those invariants individually will also uphold them when combined.  Things can get dangerous if an implementation skips bounds checks in cases where there doesn't *seem* to be any sequence of events where computations could yield out-of-range results, but if bounds checks are consistently performed invariants will be upheld.
supercat
CC BY-SA 4.0
2023-09-25T15:25:21.573Z
@MartinBerger: If there is no mechanism via which code can violate memory-layout invariants, or access things in a manner contrary to those invariants, there would be no means via which control could be transferred to any regions of address space not used by the JIT, nor that those regions of address could be written by anything other than the JIT.  Code/data segregation makes it harder to exploit opportunities to e.g. overwrite function return addresses, but on platforms that use variable-length instruction sets even ahead-of-time compiled code may be chance contain sequences that will be...
supercat
CC BY-SA 4.0
2023-09-25T15:26:00.807Z
...processed in ways useful to an attacker if execution is transferred into the middle of an instruction.
supercat
CC BY-SA 4.0
2023-09-25T18:13:53.783Z
@supercat I'm not 100% I'm following what you mean. Anyway, [JIT Spraying](https://en.wikipedia.org/wiki/JIT_spraying)  or (the potential for) [timing attacks](https://eprint.iacr.org/2006/349.pdf) have been discussed at attack vectors.
Martin Berger
CC BY-SA 4.0
2023-09-25T18:22:15.380Z
@MartinBerger: JIT spraying may be used as a means of exploiting the ability to direct code execution to inappropriate places, and if parts of the system other than the JIT have such weaknesses, JIT spraying could easily make the consequences of such weaknesses worse.  If, however, the JITted language keeps the call stack physically separate from the argument stack, and performs indirect function calls using bounds-checked indices into function tables, execution would never reach such heap-sprayed "code".  As for timing attacks...
supercat
CC BY-SA 4.0
2023-09-25T18:27:01.547Z
...I think there needs to be more focus on ways of preventing the parts of a program whose timing might be exploited having access to information people might want to extract from it.
supercat
<p>JIT compilation is an attempt to get the &quot;best of both worlds&quot; of compilation and interpretation.</p>
<p>As with an interpreter, you don't have to perform an explicit compilation step before running the program. This is useful if the program will be delivered to diverse environments, as you don't have to compile for each possible environment, or distribute a build script that the end user must run to compile the program. On the other hand, it does require that the target environment provide the execution tool; this is why we most often find it used in browser-based applications -- everyone has a browser.</p>
<p>But as the other answers say, you get the performance benefit of compiled code. There's a small performance hit from the compilation step before a particular function is executed, but this is usually negligible as it's amortized over the entire execution time of the program. Unused parts of the program never get compiled at all.</p>

CC BY-SA 4.0
2023-09-25T18:03:03.680Z
2023-09-25T18:03:03.680Z
Barmar
# What are the advantages and disadvantages of just-in-time compilation?
Posted by: username_0 on 2023-05-22
[Just-in-time compilation](https://en.wikipedia.org/wiki/Just-in-time_compilation) (JIT) is a way of implementing a language where the source code is given as input (like with a typical interpreter), but rather than being directly interpreted, (some or all of) the code is compiled. This is done, for example, in the Python interpreter PyPy, and in most browsers' JavaScript implementations.

What are the advantages and disadvantages to JIT compilation, over typical interpreters, or compilation ahead of time? When would I choose JIT over something else?
**Tags:** design-choice-comparison just-in-time
> **Comments:**
> **username_1**: This is not the usual definition of just-in-time compilation, which the Wikipedia page you linked to does have.  
> **username_2**: Without reading the wiki article, my understanding is that JIT typically involves compiling individual functions or even branches as-needed, rather than compiling the whole program upfront within the interpreter.  
> **username_3**: Note most browsers require a function to be run around ~1000 times before it will be considered for JIT, until then it's interpreted normally  
> **username_4**: Java uses a JIT compiler without having the source code available at runtime. You could argue that Java bytecode is the source code that the JIT compiles, but "source code" usually means the form in which the software is written or edited.  
> **username_5**: Hi, are the existing answers lacking? I don't want to write a new answer if any of them are acceptable (none are yet accepted); yet, my answer is going to be short: "Sometimes JIT is faster, sometimes slower; profile for your use case, and read [VM Startup Blows Hot & Cold](https://arxiv.org/abs/1602.00602v6). There are *no* known hard rules."  
---
## 5 answers
### Answer by username_6 on 2023-05-23. Score: 9
In certain cases having a JIT-compiler can be beneficial even in compiled code. Think of an image filter where you parametrize your kernel function on some runtime variables. If you compile this function just in time you have a lot more information available and thus more optimization opportunities than if you had compiled it ahead of time. For a function that is called millions of times in a hot loop this can really make a difference in terms of performance.

[There is a library](https://github.com/jmmartinez/easy-just-in-time) that aims to bring jit-compilation to C++ for this purpose.
> **Comments:**
> **username_7** (2023-08-23): Do you mean that a specific version of the function/kernel is compiled for a specific _runtime_ value of some parameter(s)? So it'd eventually need to be recompiled for other values?  
> **username_6** (2023-08-29): @PabloH Yes, exactly  
> **username_8** (2023-10-18): But even if runtime state is _not_ taken into account, JITted code can be compiled for the exact platform (processor, etc.) and so can still be faster than a compiled program which has to run on a wide variety of platforms.  
---
### Answer by username_3 on 2023-05-23. Score: 6
There is really only one advantage: Speed. Once compiled, JIT-ed code should run just as fast as native code. While there are many disadvantages the speed bonus is often worth it.

You can also incrementally apply optimizations for functions that get called a lot to reduce startup time. This can be done with real-world data about in what way the function gets called which could potentially allow more efficient optimization than when done ahead of time.

Some disadvantages include:

* Requiring taming of a dynamic type system. If everything can be any type the machine code will need to be littered with branches and not be very fast. Thus JIT-ed code will make assumptions about the types of objects based on watching them over time. However, if the types ever do not match the performance benefit is lost.
* Security vulnerabilities. Despite being made by major companies with teams of highly skilled professionals all browsers have had many security issues related to their JavaScript JIT compiler. Mostly related to making assumptions when optimizing but not checking them properly.
  + [This is a great video on the topic and why it's so hard to secure](https://www.youtube.com/watch?v=45wMEIIPsPA)
* More memory overhead. You need to keep both the original code and JITed versions of many potential type combinations of each function in memory as well as needing both primitive low level and high level versions of objects, as well as constantly needing to keep them in sync.
* Some types of events can basically invalidate some or all JITed code, requiring a slow recompile. This includes things like:
  + Changing the type of a global variable
  + Monkey patching some type
  + Self-modification
* Debugging can be harder. Typically debugging is done by just disabling the JIT portion but this would exclude bugs introduced by the JIT compilation itself.

Generally you only JIT specific functions if they are run a lot with the same argument types and the same context, so if this never happens you have no downsides except the book-keeping to keep track of how often each function is run. You can JIT in a separate thread so as not to slow down the program while compiling and just interpret normally until then.

If your language is less dynamic many of the downsides might not apply and implementation might be more straightforward.
> **Comments:**
> **username_11** (2023-05-24): "Some types of events can basically invalidate all JITed code" — I don't immediately see why the events you list should invalidate _all_ JITed code and not a small part of it?  
> **username_3** (2023-05-24): @AlexChichigin It can be really hard to prove in highly dynamic languages if a function depends on some global variable  
> **username_11** (2023-05-24): In general (contrived) case — yes, but wast majority of cases are not general and don't use global variables at all, thus most of the code stays the same, and don't get invalidated. As far as I know...  
> **username_3** (2023-05-24): The issue is, unless you can 100% prove that the global variable won't effect a function you need to regenerate it. Note the "global variable" can be something like `null`, `true`, or `object` which are also variables in Javascript  
> **username_9** (2023-09-22): Another advantage is the ability to handle source languages where a program would specify an *infinite* number of specialized functions, but only execute a finite subset that is based upon the data a program receives.  This kind of pattern can exists with generics in .NET languages like C#.  Ahead-of-time generation of machine code would be impossible because nothing in the universe could know exactly what functions would be invoked until after execution had started.  
> **username_10** (2023-09-23): JIT-compiled code can actually be *faster* than AOT-compiled code, because the JIT compiler can treat things like function parameters as constants, where the AOT compiler needs to treat them as variables.  (This does mean the code needs to be recompiled when things change, so the compiler needs to be careful about where it's applied.)  
---
### Answer by username_11 on 2023-05-24. Score: 4
> JIT is a way of implementing a language where the source code is given as input

Not necessarily at all. As we know both JVM and .NET JIT-compile byte-code and not the source code. The same is true for most WebAssembly runtimes.

As already mentioned, the main advantage of JIT over both interpretation and AOT is speed. For interpreters it's more evident as natively compiled code runs faster — almost always, there are exceptions though. Still JIT *can* outperform AOT compiled and optimized code due to availability of runtime information. That most pronounced for dynamically typed languages exactly because type (and object shape) information only fully emerges at runtime, but for statically typed languages too — as was also already mentioned, JIT can specialize (inline) and optimize algorithms for particular runtime parameters. Julia language does this with great success for many numerical algorithms.

The main user-facing disadvantage is start-up time. Again this is vividly exemplified by Julia language: before recent version 1.9 some libraries could take up to a couple of *minutes* to fully initialize and JIT, which was mitigated by employing (more) of AOT compilation at the library *install* time.

But most of other JIT-compilers including HotSpot JVM, .NET Core, V8, SpiderMonkey and others mitigate start-up latency problem via *tiered* compilation: they first run faster, less optimizing baseline JIT, and only after that a slower, better optimizing one.
> **Comments:**
> **username_9** (2023-09-22): Another performance advantage of a JIT language like Java is that programs can benefit from improvements in a JIT compiler which take place after a program is published, even if such improvements exploit hardware features that didn't exist when the program was written.  
> **username_11** (2023-09-22): Nerd remark: a _language_ can't be JIT or not, it's an _implementation_ that can use JIT or not. :) Also, AOT-compiled programs in _source code form_ also benefit from compiler improvements after they were written. And one can argue a bytecode is closer to source code than machine code, so the lines are blurry...  
> **username_9** (2023-09-22): Some languages, including Java, have historically been designed with the intention of facilitating JIT-based implementations.  Some go further and make it impossible to generate machine code for some constructs until after a program has started execution.  Compiling from source may be possible after publication, but in many the time required to compile a source-code program may exceed the time of a typical execution.  JIT-based languages are designed to make the JIT process fast.  
> **username_11** (2023-09-22): Historical note: Java _wasn't_ designed for JIT, it was only added in version 1.2. .NET was designed for JIT from the start, yep.  
---
### Answer by username_12 on 2023-09-22. Score: 4
**It's complicated.**

### Security

Compiling to native code is much less forgiving -- security-wise -- than interpreting. A stray read or write can very easily lead to an exploit.

As a result, compiling "foreign" code to native code on the fly -- whether via AOT or JIT compilation -- is fraught with peril. All mainstream JS engines have had CVEs filed against them, for example.

JIT (and AOT) compilers are therefore at a clear security disadvantage compared to interpreters when it comes to running "foreign" code.

### Size & Complexity

In general, a pure interpreter is (much) smaller than a JIT compiler, and thus much less complex. This ties in with the above security issue: the simpler, the easier to secure.

### Portability

In general, a pure interpreter is more portable than a JIT compiler. A JIT compiler must be specialized for everything target platform (both architecture and OS), whereas a pure interpreter can rely on existing toolchains to be compiled for the right platform.

Conversely, a statically compiled binary is stand-alone and can be delivered on its own to any user, whereas for interpreted or JIT compiled language, the user typically must have both the "program" (in some form) and an interpreter or JIT compiler *of the right version*. Bundlers do exist, but the resulting applications are even bigger than statically compiled applications.

### Dynamic Languages

So called dynamic languages -- ie, languages where the type of a value is not known at compile-time -- are a hard target for AOT compilers, so that in general there are only interpreters or JIT for them.

In such a situation, there's typically a start-up time vs run-time trade-off:

* An interpreter starts up near immediately but may run slower.
* A JIT compiler starts up slower -- compiling everything it comes across -- but will run faster in the long term.

The above trade-off can be alleviated (somewhat) by using tiered compilation, which is often used in JS engines, or the C# or Java runtimes. Tiered compilation is the idea of starting with the fastest start-up method, and over time to compile more and more optimized JIT code.

### Static Languages

While in theory a JIT compiler should be able to outperform anything, in practice it rarely plays out and a JIT compiler typically ends up producing a program which runs slower1 than one produced by an AOT compiler, due to a lower optimization budget.

There a specific exceptions: some workloads benefit from a run-time specialized inner loop.

The difference between theory and practice is perhaps not too surprising: in order for a JIT compiled program to outperform an AOT compiled program, it must take advantage of run-time information. This, in turn, means that a JIT *runtime* must not only JIT compile a program, but also *instrument* it to gather the necessary information, and *guard* it, in case the information it optimized for falls through.

This has 3 downsides:

1. Information gathering takes up run-time, run-time that could be put to use to actually run the program, which the JIT compiler must then make up for, somehow.
2. Hot-patching -- used both for de-optimizing and splicing in a more optimized function -- requires to leave some "scaffolding" in place. Scaffolding which takes up run-time, and which once again the JIT compiler has to make up for, somehow.
3. Guards -- used for de-optimizing -- which are extra run-time checks, which the JIT compiler has to make up for, somehow.

And of course, the very run-time used up to JIT compile also has to be made up for.

All in all, this means that JIT compiler start with a penalty2, compared to AOT compilers, and only if they uncover critical run-time information unlocking worthy optimizations will they able to produce a program which will outperform an AOT program.

And of course, very optimized AOT programs *also* use run-time information gathering for Profile Guided Optimizations, except ahead of time3 so they don't pay for it at run-time.

1 *JIT compiled pre-optimized WASM produced by AOT compilers for C, C++, Rust, or Zig, for example, is routinely 2x slower than the natively compiled program. There's indubitably some overhead from the sandboxing applied, but still...*

2 *A penalty on top of the hand tied behind their back, that is, as they typically have a much stricter "optimization budget" than AOT compilers, since they're doing the optimization work while the program is running...*

3 *PGO ahead of time may suffer from artifacts effects, yes. But then again, the latest C# JIT will splice in a non-instrumented optimized version, so that should the workload change after optimization, it also uses the wrong profile. Why? Because keeping the instrumentation in would negatively affect performance...*
> **Comments:**
> **username_9** (2023-09-22): I see no reason a JIT couldn't employ security-in-depth practices in much the same manner as interpreters.  A JIT may reap more benefit from identifying and eliminating seemingly-redundant operations, and thus many JIT compilers are more aggressive than interpreters in that regard, but a JIT which e.g. produces machine code that bounds-checks every array access could easily be less vulnerable to exploits than an interpreter which tries to identify and exploit places where checks can be omitted.  
> **username_13** (2023-09-22): JIT compilers introduce a new security problem, because JITs are *self-modifying code.* For security reasons, most modern OS use a $W \oplus X$ (write xor execute) discipline for memory locations. But a JIT generates machine code, meaning it needs to write (W) and then later jump to the written code (X). So the OS has to make memory that *may* have been compromised executable.  
> **username_12** (2023-09-23): @supercat: A JIT is inherently more difficult. You can write an interpreter in a safe language, guaranteeing that no matter how much you mess up, you're still bound by the rules of the underlying language (and its runtime). A JIT generates assembly, the ultimate unsafe language. A single mistake in interpreting an untyped register, and all soundness flies out the window.  
> **username_9** (2023-09-25): @MatthieuM.: If code consists entirely of operational sequences which validate array indices, and maintain the invariant that anything that might hold an object reference and isn't null will always be a valid object reference, operations that would uphold those invariants individually will also uphold them when combined.  Things can get dangerous if an implementation skips bounds checks in cases where there doesn't *seem* to be any sequence of events where computations could yield out-of-range results, but if bounds checks are consistently performed invariants will be upheld.  
> **username_9** (2023-09-25): @MartinBerger: If there is no mechanism via which code can violate memory-layout invariants, or access things in a manner contrary to those invariants, there would be no means via which control could be transferred to any regions of address space not used by the JIT, nor that those regions of address could be written by anything other than the JIT.  Code/data segregation makes it harder to exploit opportunities to e.g. overwrite function return addresses, but on platforms that use variable-length instruction sets even ahead-of-time compiled code may be chance contain sequences that will be...  
> **username_9** (2023-09-25): ...processed in ways useful to an attacker if execution is transferred into the middle of an instruction.  
> **username_13** (2023-09-25): @supercat I'm not 100% I'm following what you mean. Anyway, [JIT Spraying](https://en.wikipedia.org/wiki/JIT_spraying)  or (the potential for) [timing attacks](https://eprint.iacr.org/2006/349.pdf) have been discussed at attack vectors.  
> **username_9** (2023-09-25): @MartinBerger: JIT spraying may be used as a means of exploiting the ability to direct code execution to inappropriate places, and if parts of the system other than the JIT have such weaknesses, JIT spraying could easily make the consequences of such weaknesses worse.  If, however, the JITted language keeps the call stack physically separate from the argument stack, and performs indirect function calls using bounds-checked indices into function tables, execution would never reach such heap-sprayed "code".  As for timing attacks...  
> **username_9** (2023-09-25): ...I think there needs to be more focus on ways of preventing the parts of a program whose timing might be exploited having access to information people might want to extract from it.  
---
### Answer by username_14 on 2023-09-25. Score: 1
JIT compilation is an attempt to get the "best of both worlds" of compilation and interpretation.

As with an interpreter, you don't have to perform an explicit compilation step before running the program. This is useful if the program will be delivered to diverse environments, as you don't have to compile for each possible environment, or distribute a build script that the end user must run to compile the program. On the other hand, it does require that the target environment provide the execution tool; this is why we most often find it used in browser-based applications -- everyone has a browser.

But as the other answers say, you get the performance benefit of compiled code. There's a small performance hit from the compilation step before a particular function is executed, but this is usually negligible as it's amortized over the entire execution time of the program. Unused parts of the program never get compiled at all.
---
