JIT (Just-In-Time) compilation transforms a certain representation into executable code during program execution. The input can be bytecode, IR, or other representations; the compilation unit can be a function, loop, trace, or module. Whether it is triggered on the first call or pre-compiled in the background is determined by runtime policies.
The key trade-off is whether the compilation cost can be amortized by subsequent execution benefits. It requires generating fast code while avoiding overly expensive optimizations for short-lived code.
A Simple Profit Model
Let the cost of optimized compilation be C, the execution time of the old code be t0, the new code be t1, and the expected number of remaining executions be K. Ignoring other costs, time is only net-saved if K(t₀−t₁)>C.
For example, if compilation takes 4ms and saves 2μs per execution, it needs to be executed at least about 2000 times to offset the compilation cost. In reality, one must also consider profiling, guards, code space, caching, and deoptimization costs. This explains why "generated code is faster" does not necessarily make an entire short program faster.
The Break-Even Point Applies Only to the Remaining Workload
A 4ms compilation cost and a 2μs saving per call only break even at 2000 executions; to gain a net benefit, the number of executions must exceed this threshold. How many times the hotspot was executed previously does not directly enter this future profit account, because the interpretation time already spent cannot be recovered through this compilation.
compile_us=4000saved_per_call_us=2defgain(remaining_calls):returnremaining_calls*saved_per_call_us-compile_usassertgain(1000)==-2000assertgain(2000)==0assertgain(3000)==2000# Assume an additional deoptimization/recompilation cost of 600 μs.
assertgain(3000)-600==1400
Real-world decisions face uncertain remaining execution counts and failure rates. Optimizing a loop that is about to end may be counterproductive; background compilation also consumes CPU and memory bandwidth. You cannot remove compilation costs from the resource ledger of the entire service just because the compilation thread is different from the request thread. The above is merely a serial time budget model, not a direct formula for wall-clock gains during parallel compilation.
Can past hot execution repay a new compilation?
Compare two strategies from now onward. More past execution adds the same sunk cost to both, leaving future net savings unchanged. Past cost stays separate, while compilation competes only with the remaining K runs. Equality breaks even rather than profits; real systems must predict K and can be wrong.
gain=K(t0−t1)−C
Preparing the visual
Can past hot execution repay a new compilation?
Past work is shared sunk cost. Only future savings K(t₀−t₁) repay C: K=2000 breaks even, greater K wins. A hotter past does not guarantee a long future.
{"id":"jit-remaining-payback","title":"Can past hot execution repay a new compilation?","summary":"Past work is shared sunk cost. Only future savings K(t₀−t₁) repay C: K=2000 breaks even, greater K wins. A hotter past does not guarantee a long future.","height":1000,"html":"<h2 data-i18n=\"heading\"></h2><p class=\"intro\" data-i18n=\"intro\"></p><div class=\"control\"><label for=\"past\" data-i18n=\"past\"></label><output for=\"past\" id=\"past-value\"></output><input id=\"past\" type=\"range\" min=\"0\" max=\"10000\" value=\"5000\" step=\"1000\"></div><div class=\"control\"><label for=\"future\" data-i18n=\"future\"></label><output for=\"future\" id=\"future-value\"></output><input id=\"future\" type=\"range\" min=\"0\" max=\"5000\" value=\"2000\" step=\"100\"></div><div id=\"costs\" class=\"table\"></div><div class=\"metrics\"><div><span data-i18n=\"old\"></span><strong id=\"old\"></strong></div><div><span data-i18n=\"new\"></span><strong id=\"new\"></strong></div><div><span data-i18n=\"gain\"></span><strong id=\"gain\"></strong></div></div><p id=\"equation\" class=\"formula\"></p><details class=\"scope\"><summary data-i18n=\"scopeLabel\"></summary><p data-i18n=\"scope\"></p></details>","css":".intro{margin:8px 0 18px;color:var(--muted);font-size:14px}.caption,.hint{font-size:12px;color:var(--muted);margin-top:12px}.control{display:grid;grid-template-columns:minmax(0,1fr) auto;gap:6px 12px;align-items:center;margin:14px 0;font-size:13px}.control label{font-size:13px}.control output{color:var(--accent);min-width:5em;text-align:right;font-variant-numeric:tabular-nums}.control input{grid-column:1/-1;width:100%;margin:0}.presets{display:flex;gap:4px;align-items:stretch}.presets button{flex:1 1 0;min-width:0;overflow-wrap:anywhere;font-size:13px}.actions{display:flex;gap:8px;flex-wrap:wrap;margin:14px 0}.actions button{flex:1;min-width:0;font-size:13px;min-height:42px}.copy{display:grid;font-size:14px;margin:16px 0}.copy>*{grid-area:1/1;margin:0}.reserve{visibility:hidden}.scope{margin-top:14px;color:var(--muted);font-size:12px}.scope summary{padding:8px 0;cursor:pointer}.scope p{margin-top:10px}.two{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:12px;margin:16px 0}.card{border:1px solid var(--rule);border-radius:9px;padding:14px;min-width:0}.card header{font-size:14px;font-weight:600;margin-bottom:10px}.card button{font-size:12px;width:100%;min-width:0}.card small{display:block;font-size:12px;color:var(--muted)}.card code{font-size:12px;overflow-wrap:anywhere}.metrics{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:12px;margin:16px 0}.metrics>div{border-left:2px solid var(--accent);padding-left:10px;min-width:0}.metrics span{display:block;min-height:3.2em;color:var(--muted);font-size:12px}.metrics strong{font-size:23px;display:block;font-weight:500;overflow-wrap:anywhere;font-variant-numeric:tabular-nums}.formula{border-top:1px solid var(--rule);padding-top:12px;margin-top:14px;font-size:13px;font-variant-numeric:tabular-nums;overflow-wrap:anywhere}.value{font:500 20px var(--viz-font-ui)}.legend{display:flex;flex-wrap:wrap;gap:6px 14px;font-size:12px;color:var(--muted);margin:12px 0}.legend span:before{content:'';display:inline-block;width:8px;height:8px;border-radius:2px;background:var(--accent);margin-right:6px}.legend .second:before{background:var(--second)}.check{display:flex;align-items:center;gap:8px;font-size:13px;margin:12px 0}.check input{width:16px;height:16px;flex-shrink:0;accent-color:var(--accent)}select,input[type=text],input[type=number]{background:var(--paper);color:var(--ink);font:inherit;font-size:13px;padding:10px;border:1px solid var(--rule);border-radius:7px;max-width:100%;min-width:0}select{width:100%;min-height:42px}.band{height:20px;display:flex;background:var(--surface);border-radius:4px;overflow:hidden}.band i{height:100%;display:block;background:var(--accent);transition:width .25s}.band i.second{background:var(--second)}button:disabled{cursor:default}.status{font-size:13px;min-height:4.8em}.mono{font-family:var(--viz-font-mono);font-variant-numeric:tabular-nums}.muted{color:var(--muted)}\n@media(max-width:480px){.two{gap:8px}.card{padding:12px 9px}.actions button,.presets button{font-size:12px}.metrics strong{font-size:20px}.metrics{gap:8px}}\n@media(prefers-reduced-motion:reduce){*,*::before,*::after{transition:none!important;animation:none!important}}\n.track-row{margin:16px 0}.track-row>span{display:block;font-size:12px;color:var(--muted);margin-bottom:7px}.track{display:flex;gap:5px;min-width:0}.cell{flex:1;min-width:0;min-height:48px;border:1px solid var(--rule);border-radius:5px;background:var(--surface);display:flex;align-items:center;justify-content:center;text-align:center;padding:5px 3px;font-size:12px;overflow-wrap:anywhere;font-variant-numeric:tabular-nums}.cell.on{background:var(--accent-soft);border-color:var(--accent)}.cell.gold{background:var(--second-soft);border-color:var(--second)}.cell.empty{border-style:dashed;color:var(--muted)}.cell.error{border-color:var(--second);text-decoration:line-through}.choices{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:6px;margin:14px 0}.choices button{min-width:0;padding:10px 4px;font-size:12px;overflow-wrap:anywhere}.choices button[aria-current=true]{border-color:var(--accent);background:var(--accent-soft)}.relation{display:grid;grid-template-columns:minmax(0,1fr) 28px minmax(0,1fr);align-items:center;gap:6px;margin:12px 0}.relation .cell{min-height:72px}.relation>i{text-align:center;font-style:normal;color:var(--accent)}.readout{font-size:13px;white-space:pre-line;min-height:6.4em;overflow-wrap:anywhere}.plot{position:relative;border-left:1px solid var(--rule);border-bottom:1px solid var(--rule);padding:8px 0;margin:18px 0}.plot-row{height:28px;position:relative;margin:4px 0;background:var(--surface)}.plot-row i{position:absolute;inset-block:5px;background:var(--accent);min-width:2px;transition:width .2s,left .2s}.plot-row b{position:relative;padding:4px 6px;font:11px/28px var(--viz-font-ui);background:var(--paper);border-radius:3px}.axis{display:flex;justify-content:space-between;font-size:11px;color:var(--muted)}\n\n/* Action labels must remain readable across locales and narrow embeds. */\n.actions{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:8px}\n.actions button{overflow-wrap:anywhere;white-space:normal;min-height:58px;padding:8px}\nbutton{overflow-wrap:anywhere}\n@media(min-width:640px){.actions{grid-template-columns:repeat(3,minmax(0,1fr))}}\n\n.metrics span{overflow-wrap:anywhere}\n.panel{border:1px solid var(--rule);border-radius:9px;padding:12px;margin:14px 0;min-width:0}.panel h3{font-size:13px;margin:0 0 10px;color:var(--muted)}.source{font:13px/1.8 var(--viz-font-mono);white-space:pre-wrap;overflow-wrap:anywhere;margin:12px 0}.source mark{background:var(--accent-soft);color:var(--ink);border-bottom:2px solid var(--accent)}.source .fault{background:var(--second-soft);border-color:var(--second)}.diagram{display:block;width:100%;height:auto;max-height:320px;margin:14px 0;overflow:visible}.diagram text{fill:var(--ink);font:12px var(--viz-font-ui)}.diagram .edge{stroke:var(--muted);stroke-width:1.5;fill:none}.diagram .edge.on{stroke:var(--accent);stroke-width:3}.diagram .node{fill:var(--surface);stroke:var(--rule);stroke-width:1.5}.diagram .node.on{fill:var(--accent-soft);stroke:var(--accent)}.diagram .node.gold{fill:var(--second-soft);stroke:var(--second)}.log{font:12px/1.8 var(--viz-font-mono);white-space:pre-line;overflow-wrap:anywhere;min-height:7.2em}.table{display:grid;gap:5px;margin:14px 0}.table .tr{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:5px}.table .tr>*{padding:8px 5px;background:var(--surface);border-radius:4px;min-width:0;overflow-wrap:anywhere;font-size:12px}.table .tr .on{background:var(--accent-soft)}.table .tr .gold{background:var(--second-soft)}.choices button{min-height:44px}.metrics strong{min-height:1.6em}.code-line{display:block}.code-line.on{background:var(--accent-soft);border-left:2px solid var(--accent);padding-left:6px}.status-badge{font-size:12px;color:var(--accent)}.tile-grid{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:6px}.tile-grid .cell{min-height:40px}.actions button{min-height:48px}.form-field{display:grid;gap:8px;margin:14px 0;font-size:13px}.form-field input{width:100%;font-family:var(--viz-font-ui)}.metrics.three{grid-template-columns:repeat(3,minmax(0,1fr))}.formula{min-height:3.2em}.presets{flex-wrap:wrap}.presets button{flex-basis:25%;min-height:44px}\n\n.metrics{grid-template-columns:repeat(3,minmax(0,1fr))}.metrics strong{font-size:18px}#costs .tr{grid-template-columns:1fr 2fr}.costbar{height:18px;background:var(--accent);display:block;border-radius:3px}","js":"const $=s=>document.querySelector(s);const set=(id,v)=>$('#'+id).textContent=v;const t=k=>viz.t(k);function cells(id,values){$('#'+id).replaceChildren(...values.map(v=>{const e=document.createElement('div');e.className='cell '+(v.cls||'');e.textContent=v.text;e.title=v.title||v.text;return e;}));}function pressed(mode){document.querySelectorAll('[data-mode]').forEach(e=>e.setAttribute('aria-pressed',String(e.dataset.mode===mode)));}const esc=s=>String(s).replace(/[&<>\"']/g,c=>({'&':'&','<':'<','>':'>','\"':'"',\"'\":'''}[c]));const fmt=s=>'{'+[...s].sort().join(', ')+'}';function graph(id,nodes,edges){const el=$('#'+id);el.setAttribute('viewBox','0 0 360 200');el.innerHTML='<defs><marker id=\"arrow-'+id+'\" viewBox=\"0 0 10 10\" refX=\"8\" refY=\"5\" markerWidth=\"5\" markerHeight=\"5\" orient=\"auto-start-reverse\"><path d=\"M 0 0 L 10 5 L 0 10 z\" fill=\"var(--muted)\"/></marker></defs>'+edges.map(e=>{const a=nodes[e[0]],b=nodes[e[1]],dx=b.x-a.x,dy=b.y-a.y,d=Math.hypot(dx,dy)||1;if(edges.some(r=>r[0]===e[1]&&r[1]===e[0])){const nx=-dy/d,ny=dx/d;return '<path class=\"edge '+(e[2]?'on':'')+'\" d=\"M '+(a.x+dx/d*22+nx*12)+' '+(a.y+dy/d*22+ny*12)+' Q '+((a.x+b.x)/2+nx*38)+' '+((a.y+b.y)/2+ny*38)+' '+(b.x-dx/d*25+nx*12)+' '+(b.y-dy/d*25+ny*12)+'\" marker-end=\"url(#arrow-'+id+')\"/>';}return '<line class=\"edge '+(e[2]?'on':'')+'\" x1=\"'+(a.x+dx/d*25)+'\" y1=\"'+(a.y+dy/d*25)+'\" x2=\"'+(b.x-dx/d*28)+'\" y2=\"'+(b.y-dy/d*28)+'\" marker-end=\"url(#arrow-'+id+')\"/>';}).join('')+nodes.map(n=>'<g><circle class=\"node '+(n.cls||'')+'\" cx=\"'+n.x+'\" cy=\"'+n.y+'\" r=\"25\"/><text x=\"'+n.x+'\" y=\"'+(n.y+4)+'\" text-anchor=\"middle\">'+esc(n.name)+'</text>'+(n.sub?'<text x=\"'+n.x+'\" y=\"'+(n.y+43)+'\" text-anchor=\"middle\">'+esc(n.sub)+'</text>':'')+'</g>').join('');}function table(id,rows){$('#'+id).replaceChildren(...rows.map(r=>{const row=document.createElement('div');row.className='tr';r.forEach(x=>{const c=document.createElement('div');if(typeof x==='object'){c.textContent=x.text;c.className=x.cls||'';}else c.textContent=x;row.append(c)});return row;}));}function draw(){const past=+$('#past').value,k=+$('#future').value,old=k*.003,ne=4+k*.001,gain=old-ne;set('past-value',past);set('future-value',k);table('costs',[['old',old.toFixed(1)+' ms'],['new',ne.toFixed(1)+' ms'],['past',(past*.003).toFixed(1)+' ms (both)']]);$('#costs').querySelectorAll('.tr').forEach((e,i)=>{const bar=document.createElement('i');bar.className='costbar';bar.style.width=([old,ne,past*.003][i]/30*100)+'%';e.children[1].append(bar)});set('old',old.toFixed(1));set('new',ne.toFixed(1));set('gain',gain.toFixed(1));set('equation',k+' × (3−1) μs − 4000 μs = '+(k*2-4000)+' μs');$('#gain').dataset.profitable=String(gain>0);}document.querySelectorAll('input').forEach(e=>e.oninput=draw);draw();","audio":false,"strings":{"future":"Remaining executions K","gain":"Net saving / ms","heading":"Can past hot execution repay a new compilation?","intro":"Compilation costs4ms; old/new executions cost3/1μs. Change past and remaining runs independently to see what determines payoff.","new":"Compile and use new / ms","old":"Keep old code / ms","past":"Past executions","reset":"Reset","scope":"Serial-time model with fixed costs; no background contention, profiling, guards, invalidation, code space or actual JIT prediction policy.","scopeLabel":"Model scope and assumptions"}}
Tiering: Reduce Startup Cost First, Then Invest in Hotspots
Tier
Goal
Common Work
Interpreter
Fast start of execution
Decoding and dispatching, collecting partial feedback
Baseline Compiler
Eliminate interpretation overhead with lower compilation cost
Inlining, type specialization, redundancy elimination, etc.
Not all runtimes have all tiers, nor is it guaranteed that every function passes through each tier. Strategies can refer to call counts, loop back-edges, code size, existing feedback, and system resources; invalidation or environmental changes may also trigger recompilation.
Taking V8's public design as an example, Ignition is the interpreter, Sparkplug is the baseline compiler that generates machine code from bytecode; Maglev's design demonstrates how an intermediate optimization layer balances compilation speed and code quality. These are used to understand roles, not to treat the tier configuration in a specific historical article as a fixed pipeline for all versions. Sparkplug, Maglev
Feedback and Inline Caching: Remembering What Happened at This Access Point
Suppose the same source code location repeatedly executes obj.x. Generic property lookup may need to interpret object layout, property descriptors, and inheritance relationships. Inline caching records conditions observed at this access point, such as the shape, along with corresponding fast operations.
Monomorphic caches retain one shape, polymorphic caches retain a few shapes; after observing many different shapes, one can switch to a lookup strategy suitable for high polymorphism. Different systems use different structures and thresholds.
Caches are usually associated with the access point and its context, not treating "the type of the last arbitrary object" as a global answer. When object layout, prototype chain, or method definitions change, guards or dependency invalidation mechanisms are needed to prevent the cache from still pointing to outdated implementations. ICs can also exist in the interpreter; it is not only optimized machine code that can use them.
Speculative Optimization: Assumptions Must Be Explicitly Protected
JIT observes that variables are mostly integers and can generate an integer fast path, but the language may allow floats, strings, or big integers to appear next. Guards verify relevant preconditions; on mismatch, execution enters a generic slow path or reverts to a lower optimization tier.
AOT can also use speculation through static analysis, PGO, code versioning, and runtime checks. The difference lies in the timing of feedback acquisition and code generation, not that AOT never knows runtime information while only JIT can optimize.
Deoptimization: Restoring State at the Language Level
Optimization places variables in registers, removes redundant values, inlines functions, and even splits an object into several scalars. When speculation fails, the runtime must reconstruct the logical state needed for subsequent execution: current bytecode position, local variables, operand stack, inlined call frames, and necessary objects.
Therefore, deoptimization metadata records how to restore a lower-level representation from the current machine state. Some values can be read directly, while others need to be rematerialized according to the records. Saving just a few registers is insufficient to restore a program that has undergone inlining and scalar replacement.
Recovery points must also consider side effects. Suppose a deduction has already been executed, and then a guard fails; you cannot restart from the beginning of the function, as that would cause the deduction to happen again. Deoptimization must continue to a semantically correct position; it is not accurate to simply say "go back to the interpreter to redo all operations."
A Deoptimization State Model with Side Effects
Assume the teaching language's integers can grow to arbitrary precision, while the optimized version only handles the int32 range. The following Python code simulates "checking preconditions before operation, and restoring i and total upon failure"; it does not generate machine code, nor does it use Python interpreter's own JIT behavior as evidence.
MIN32, MAX32=-(1<<31), (1<<31)-1defresume_generic(values,i,total):whilei<len(values):total+=values[i]i+=1returntotaldefspecialized(values,effects):effects.append("begin")total=0i=0whilei<len(values):item=values[i]candidate=total+item# Model uses arbitrary precision to calculate guard condition first
ifnot(MIN32<=item<=MAX32andMIN32<=candidate<=MAX32):snapshot={"pc":"loop_add","i":i,"total":total}returnresume_generic(values,i,total), snapshottotal=candidatei+=1returntotal, Noneeffects=[]result, snapshot=specialized([MAX32,1,2],effects)assertresult==MAX32+3assertsnapshot=={"pc":"loop_add","i":1,"total":MAX32}asserteffects==["begin"]result, snapshot=specialized([10,20],[])assertresult==30andsnapshotisNone
When the guard fails, i=1 indicates that values[1] has not yet been added to total. If restored to i=2, the value 1 would be missed; if restored to the beginning of the function, begin would be executed again. Real-world generators might use machine addition with overflow flags followed by branching, but deoptimization metadata must be able to restore the correct original operands and recovery position, and cannot pass off already wrapped machine results as language results.
If another function was inlined here, there might be only one machine stack frame on the low-level stack, but logically two call frames need to be restored. If an object has already been scalar replaced, it needs to be rematerialized while maintaining the required identity relationships; "generating a new object with identical fields" does not always maintain reference equality semantics. In V8 Maglev's design, frame state recording is a key part connecting speculative optimization with correct recovery. Maglev Deoptimization
Where can deoptimization resume without repeating effects?
A resume state must identify the unexecuted operation, computed values and completed effects. Two intentionally wrong strategies remain comparable: restarting may preserve the sum while repeating begin; skipping may preserve effect count while losing a term. Correct continuation requires checking value, position and effects together.
Preparing the visual
Where can deoptimization resume without repeating effects?
The second addition exceeds int32; its pre-operation guard fails. Restore i=1,total=2147483647 and preserve the single begin effect. Restart repeats it; skipping i loses a value.
{"id":"deoptimization-resume-state","title":"Where can deoptimization resume without repeating effects?","summary":"The second addition exceeds int32; its pre-operation guard fails. Restore i=1,total=2147483647 and preserve the single begin effect. Restart repeats it; skipping i loses a value.","height":1000,"html":"<h2 data-i18n=\"heading\"></h2><p class=\"intro\" data-i18n=\"intro\"></p><div class=\"presets\" role=\"group\" data-i18n-label=\"mode\"><button data-mode=\"correct\" data-i18n=\"correct\"></button><button data-mode=\"restart\" data-i18n=\"restart\"></button><button data-mode=\"skip\" data-i18n=\"skip\"></button></div><p class=\"source\">begin();\nfor i in 0..3: total += values[i]</p><section class=\"panel\"><h3 data-i18n=\"state\"></h3><div id=\"state\"></div></section><div id=\"items\" class=\"track\"></div><div class=\"metrics\"><div><span data-i18n=\"total\"></span><strong id=\"total\"></strong></div><div><span data-i18n=\"effects\"></span><strong id=\"effects\"></strong></div></div><div class=\"copy\"><p id=\"explanation\" role=\"status\"></p><p class=\"reserve\" aria-hidden=\"true\" data-i18n=\"ok\"></p><p class=\"reserve\" aria-hidden=\"true\" data-i18n=\"bad\"></p></div><details class=\"scope\"><summary data-i18n=\"scopeLabel\"></summary><p data-i18n=\"scope\"></p></details>","css":".intro{margin:8px 0 18px;color:var(--muted);font-size:14px}.caption,.hint{font-size:12px;color:var(--muted);margin-top:12px}.control{display:grid;grid-template-columns:minmax(0,1fr) auto;gap:6px 12px;align-items:center;margin:14px 0;font-size:13px}.control label{font-size:13px}.control output{color:var(--accent);min-width:5em;text-align:right;font-variant-numeric:tabular-nums}.control input{grid-column:1/-1;width:100%;margin:0}.presets{display:flex;gap:4px;align-items:stretch}.presets button{flex:1 1 0;min-width:0;overflow-wrap:anywhere;font-size:13px}.actions{display:flex;gap:8px;flex-wrap:wrap;margin:14px 0}.actions button{flex:1;min-width:0;font-size:13px;min-height:42px}.copy{display:grid;font-size:14px;margin:16px 0}.copy>*{grid-area:1/1;margin:0}.reserve{visibility:hidden}.scope{margin-top:14px;color:var(--muted);font-size:12px}.scope summary{padding:8px 0;cursor:pointer}.scope p{margin-top:10px}.two{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:12px;margin:16px 0}.card{border:1px solid var(--rule);border-radius:9px;padding:14px;min-width:0}.card header{font-size:14px;font-weight:600;margin-bottom:10px}.card button{font-size:12px;width:100%;min-width:0}.card small{display:block;font-size:12px;color:var(--muted)}.card code{font-size:12px;overflow-wrap:anywhere}.metrics{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:12px;margin:16px 0}.metrics>div{border-left:2px solid var(--accent);padding-left:10px;min-width:0}.metrics span{display:block;min-height:3.2em;color:var(--muted);font-size:12px}.metrics strong{font-size:23px;display:block;font-weight:500;overflow-wrap:anywhere;font-variant-numeric:tabular-nums}.formula{border-top:1px solid var(--rule);padding-top:12px;margin-top:14px;font-size:13px;font-variant-numeric:tabular-nums;overflow-wrap:anywhere}.value{font:500 20px var(--viz-font-ui)}.legend{display:flex;flex-wrap:wrap;gap:6px 14px;font-size:12px;color:var(--muted);margin:12px 0}.legend span:before{content:'';display:inline-block;width:8px;height:8px;border-radius:2px;background:var(--accent);margin-right:6px}.legend .second:before{background:var(--second)}.check{display:flex;align-items:center;gap:8px;font-size:13px;margin:12px 0}.check input{width:16px;height:16px;flex-shrink:0;accent-color:var(--accent)}select,input[type=text],input[type=number]{background:var(--paper);color:var(--ink);font:inherit;font-size:13px;padding:10px;border:1px solid var(--rule);border-radius:7px;max-width:100%;min-width:0}select{width:100%;min-height:42px}.band{height:20px;display:flex;background:var(--surface);border-radius:4px;overflow:hidden}.band i{height:100%;display:block;background:var(--accent);transition:width .25s}.band i.second{background:var(--second)}button:disabled{cursor:default}.status{font-size:13px;min-height:4.8em}.mono{font-family:var(--viz-font-mono);font-variant-numeric:tabular-nums}.muted{color:var(--muted)}\n@media(max-width:480px){.two{gap:8px}.card{padding:12px 9px}.actions button,.presets button{font-size:12px}.metrics strong{font-size:20px}.metrics{gap:8px}}\n@media(prefers-reduced-motion:reduce){*,*::before,*::after{transition:none!important;animation:none!important}}\n.track-row{margin:16px 0}.track-row>span{display:block;font-size:12px;color:var(--muted);margin-bottom:7px}.track{display:flex;gap:5px;min-width:0}.cell{flex:1;min-width:0;min-height:48px;border:1px solid var(--rule);border-radius:5px;background:var(--surface);display:flex;align-items:center;justify-content:center;text-align:center;padding:5px 3px;font-size:12px;overflow-wrap:anywhere;font-variant-numeric:tabular-nums}.cell.on{background:var(--accent-soft);border-color:var(--accent)}.cell.gold{background:var(--second-soft);border-color:var(--second)}.cell.empty{border-style:dashed;color:var(--muted)}.cell.error{border-color:var(--second);text-decoration:line-through}.choices{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:6px;margin:14px 0}.choices button{min-width:0;padding:10px 4px;font-size:12px;overflow-wrap:anywhere}.choices button[aria-current=true]{border-color:var(--accent);background:var(--accent-soft)}.relation{display:grid;grid-template-columns:minmax(0,1fr) 28px minmax(0,1fr);align-items:center;gap:6px;margin:12px 0}.relation .cell{min-height:72px}.relation>i{text-align:center;font-style:normal;color:var(--accent)}.readout{font-size:13px;white-space:pre-line;min-height:6.4em;overflow-wrap:anywhere}.plot{position:relative;border-left:1px solid var(--rule);border-bottom:1px solid var(--rule);padding:8px 0;margin:18px 0}.plot-row{height:28px;position:relative;margin:4px 0;background:var(--surface)}.plot-row i{position:absolute;inset-block:5px;background:var(--accent);min-width:2px;transition:width .2s,left .2s}.plot-row b{position:relative;padding:4px 6px;font:11px/28px var(--viz-font-ui);background:var(--paper);border-radius:3px}.axis{display:flex;justify-content:space-between;font-size:11px;color:var(--muted)}\n\n/* Action labels must remain readable across locales and narrow embeds. */\n.actions{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:8px}\n.actions button{overflow-wrap:anywhere;white-space:normal;min-height:58px;padding:8px}\nbutton{overflow-wrap:anywhere}\n@media(min-width:640px){.actions{grid-template-columns:repeat(3,minmax(0,1fr))}}\n\n.metrics span{overflow-wrap:anywhere}\n.panel{border:1px solid var(--rule);border-radius:9px;padding:12px;margin:14px 0;min-width:0}.panel h3{font-size:13px;margin:0 0 10px;color:var(--muted)}.source{font:13px/1.8 var(--viz-font-mono);white-space:pre-wrap;overflow-wrap:anywhere;margin:12px 0}.source mark{background:var(--accent-soft);color:var(--ink);border-bottom:2px solid var(--accent)}.source .fault{background:var(--second-soft);border-color:var(--second)}.diagram{display:block;width:100%;height:auto;max-height:320px;margin:14px 0;overflow:visible}.diagram text{fill:var(--ink);font:12px var(--viz-font-ui)}.diagram .edge{stroke:var(--muted);stroke-width:1.5;fill:none}.diagram .edge.on{stroke:var(--accent);stroke-width:3}.diagram .node{fill:var(--surface);stroke:var(--rule);stroke-width:1.5}.diagram .node.on{fill:var(--accent-soft);stroke:var(--accent)}.diagram .node.gold{fill:var(--second-soft);stroke:var(--second)}.log{font:12px/1.8 var(--viz-font-mono);white-space:pre-line;overflow-wrap:anywhere;min-height:7.2em}.table{display:grid;gap:5px;margin:14px 0}.table .tr{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:5px}.table .tr>*{padding:8px 5px;background:var(--surface);border-radius:4px;min-width:0;overflow-wrap:anywhere;font-size:12px}.table .tr .on{background:var(--accent-soft)}.table .tr .gold{background:var(--second-soft)}.choices button{min-height:44px}.metrics strong{min-height:1.6em}.code-line{display:block}.code-line.on{background:var(--accent-soft);border-left:2px solid var(--accent);padding-left:6px}.status-badge{font-size:12px;color:var(--accent)}.tile-grid{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:6px}.tile-grid .cell{min-height:40px}.actions button{min-height:48px}.form-field{display:grid;gap:8px;margin:14px 0;font-size:13px}.form-field input{width:100%;font-family:var(--viz-font-ui)}.metrics.three{grid-template-columns:repeat(3,minmax(0,1fr))}.formula{min-height:3.2em}.presets{flex-wrap:wrap}.presets button{flex-basis:25%;min-height:44px}\n\n#state{font:13px/1.8 var(--viz-font-ui);min-height:9em;white-space:pre-line}.metrics strong{font-size:18px}","js":"const $=s=>document.querySelector(s);const set=(id,v)=>$('#'+id).textContent=v;const t=k=>viz.t(k);function cells(id,values){$('#'+id).replaceChildren(...values.map(v=>{const e=document.createElement('div');e.className='cell '+(v.cls||'');e.textContent=v.text;e.title=v.title||v.text;return e;}));}function pressed(mode){document.querySelectorAll('[data-mode]').forEach(e=>e.setAttribute('aria-pressed',String(e.dataset.mode===mode)));}const esc=s=>String(s).replace(/[&<>\"']/g,c=>({'&':'&','<':'<','>':'>','\"':'"',\"'\":'''}[c]));const fmt=s=>'{'+[...s].sort().join(', ')+'}';function graph(id,nodes,edges){const el=$('#'+id);el.setAttribute('viewBox','0 0 360 200');el.innerHTML='<defs><marker id=\"arrow-'+id+'\" viewBox=\"0 0 10 10\" refX=\"8\" refY=\"5\" markerWidth=\"5\" markerHeight=\"5\" orient=\"auto-start-reverse\"><path d=\"M 0 0 L 10 5 L 0 10 z\" fill=\"var(--muted)\"/></marker></defs>'+edges.map(e=>{const a=nodes[e[0]],b=nodes[e[1]],dx=b.x-a.x,dy=b.y-a.y,d=Math.hypot(dx,dy)||1;if(edges.some(r=>r[0]===e[1]&&r[1]===e[0])){const nx=-dy/d,ny=dx/d;return '<path class=\"edge '+(e[2]?'on':'')+'\" d=\"M '+(a.x+dx/d*22+nx*12)+' '+(a.y+dy/d*22+ny*12)+' Q '+((a.x+b.x)/2+nx*38)+' '+((a.y+b.y)/2+ny*38)+' '+(b.x-dx/d*25+nx*12)+' '+(b.y-dy/d*25+ny*12)+'\" marker-end=\"url(#arrow-'+id+')\"/>';}return '<line class=\"edge '+(e[2]?'on':'')+'\" x1=\"'+(a.x+dx/d*25)+'\" y1=\"'+(a.y+dy/d*25)+'\" x2=\"'+(b.x-dx/d*28)+'\" y2=\"'+(b.y-dy/d*28)+'\" marker-end=\"url(#arrow-'+id+')\"/>';}).join('')+nodes.map(n=>'<g><circle class=\"node '+(n.cls||'')+'\" cx=\"'+n.x+'\" cy=\"'+n.y+'\" r=\"25\"/><text x=\"'+n.x+'\" y=\"'+(n.y+4)+'\" text-anchor=\"middle\">'+esc(n.name)+'</text>'+(n.sub?'<text x=\"'+n.x+'\" y=\"'+(n.y+43)+'\" text-anchor=\"middle\">'+esc(n.sub)+'</text>':'')+'</g>').join('');}function table(id,rows){$('#'+id).replaceChildren(...rows.map(r=>{const row=document.createElement('div');row.className='tr';r.forEach(x=>{const c=document.createElement('div');if(typeof x==='object'){c.textContent=x.text;c.className=x.cls||'';}else c.textContent=x;row.append(c)});return row;}));}let mode='correct';function draw(){const values=[2147483647,1,2];let total=2147483647,i=1,effects=1;if(mode==='restart'){i=0;total=0;effects++}if(mode==='skip')i++;set('state','guard: i=1, total=2147483647\\nresume: i='+i+', total='+total+'\\nbegin already executed = 1');cells('items',values.map((v,j)=>({text:'['+j+'] '+v,cls:j>=i?'on':mode==='skip'&&j===1?'gold':'empty'})));for(;i<values.length;i++)total+=values[i];set('total',total);set('effects',effects);set('explanation',t(total===2147483650&&effects===1?'ok':'bad'));pressed(mode);}document.querySelectorAll('[data-mode]').forEach(b=>b.onclick=()=>{mode=b.dataset.mode;draw()});draw();","audio":false,"strings":{"bad":"Resume position or effect count is wrong; checking only the numeric result can miss duplication.","correct":"Resume current item","effects":"begin effect count","heading":"Where can deoptimization resume without repeating effects?","intro":"Array [2147483647,1,2], unbounded language integers. Choose a resume strategy and compare optimized progress, restored state and effects.","mode":"Resume strategy","ok":"Result and effect count both match generic execution.","reset":"Reset","restart":"Wrong: restart function","scope":"Teaching execution with a fixed overflow point, no machine code, inlined frames or object materialization. Explains one resume point and effect counts.","scopeLabel":"Model scope and assumptions","skip":"Wrong: skip current item","state":"State at failed guard","total":"Final total"}}
OSR: Switching Execution Versions Before Function Return
A loop that is called only once but runs for a long time is too late to use optimized code by the time the next function call happens. On-Stack Replacement (OSR) transfers the currently active state to another version of the code at a suitable point during execution.
Common use cases include entering optimized code from an interpreted or baseline hot loop; some runtimes also use the broader term stack replacement to describe downgrade transformations. It shares the state transition problem with deoptimization, but cannot be simply defined as "a special case of deoptimization": upgrading OSR is precisely a transfer to a more optimized version.
OSR Entry Points Must Specify the Source of Each Active Value
For example, if the loop has already processed values[0:3] and is about to process index 3, the new code must receive the current array reference, i=3, and the accumulated total; it cannot use the standard function entry initialization of i=0 and total=0. Temporary values no longer used outside the loop do not need to be transferred, but values needed later must have a definite source.
Transfer Information
Why It Is Needed
Result of Absence
Restored/Entered Program Position
Distinguish between loop header, middle of loop body, and back-edge
Repetition or skipping of operations
Active Local Variables and Operands
Continue unfinished calculations
Use of incorrect initial values
Object and GC Reference Locations
Allow collectors to still find active objects
Objects incorrectly collected or updates missed
Exception and Inline Call State
Maintain throw, unwind, and debugging semantics
Exceptions falling into wrong handling scopes
OSR compilation may also find that assumptions no longer apply during generation, requiring verification of corresponding conditions upon entry. Heat counting is responsible for deciding if it is worth attempting, while entry guards confirm that it is safe to switch at this moment; the two cannot replace each other.
Code Cache: Safe Publication and Recycling After Generation
JIT needs to allocate code space, handle relocations, register GC and exception unwinding information, and publish executable code and synchronize instruction caches according to platform requirements. It does not need to write each function as a .so file first and then dlopen; runtimes can usually directly manage code and symbols in memory.
Before recycling code, it must be confirmed that no threads are still executing it, and return addresses, function entries, or cache references cannot be dangling. What allocator, eviction strategy, and write-execute permission conversion to use is determined by the implementation and platform constraints; there is no unified rule that all code caches use slab + LRU.
Evaluate by Separating Several Stages
Metric
Problems It Can Expose
Cold Start and First Request
Parsing, loading, and initial compilation costs
Warm-up Process
Upgrade speed, compilation thread contention
Steady-state Throughput and Tail Latency
Code quality, GC, scheduling, and mismatches
Deoptimization and Recompilation Counts
Unstable assumptions, changes in input shapes
Code and Metadata Space
Bloat from specialized versions, delayed recycling
Do not just measure a short loop after warm-up and generalize the results to the overall performance of the service. AOT and JIT can also be combined, reducing runtime work through caching, offline profiles, or pre-compiled base code.