即時編譯

在計算機技術中，即時編譯而不是在執行之前進行編譯。通常，這包括原始碼或更常見的位元組碼到機器碼的轉換，然後直接執行。實現JIT編譯器的系統通常會不斷地分析正在執行的程式碼，並確定程式碼的某些部分，在這些部分中，編譯或重新編譯所獲得的加速將超過編譯該程式碼的開銷。JIT編譯是兩種傳統的機器程式碼翻譯方法——的開銷，結合了編譯程式碼的速度與解釋的靈活性。JIT編譯是動態編譯的一種形式，允許，比如動態重編譯和特定於微架構的加速#重定向 ——因此，在理論上，JIT編譯比靜態編譯能夠產生更快的執行速度。解釋和JIT編譯特別適合於動態程式語言，因為執行時系統可以處理的資料型別並實施安全保證。應用.JIT編譯可以應用於某些程式，也可以用於某些能力，特別是動態能力，如正則表示式。例如，一個文字編輯器可以把執行時提供的正則表示式編譯成機器碼，從而更快地進行匹配——這不能提前完成，因為pattern只在執行時提供。一些現代的執行時環境依賴JIT編譯來實現高速程式碼執行，包括大多數Java實現，以及微軟的.NET框架。類似地，許多正則表示式庫都具有對正則表示式進行JIT編譯的功能，可以編譯成位元組碼，也可以編譯成機器碼。JIT編譯也用於一些模擬器中，以便將機器程式碼從一個CPU體系結構轉換到另一個CPU體系結構。JIT編譯的一個常見實現是首先進行AOT編譯，把原始碼編譯成位元組碼，而不是解釋位元組碼。與解釋相比，這提高了執行時效能，但代價是編譯造成的延遲。與直譯器一樣，JIT編譯器不斷地進行翻譯，但是對編譯後的程式碼進行快取可以最大限度地減少在給定執行期間將來執行相同程式碼的延遲。概述.在位元組碼編譯的系統中，原始碼被轉換為稱為位元組碼的中間表示形式。位元組碼不是任何特定計算機的機器程式碼，可以在計算機體系結構之間移植。然後可以在虛擬機器上解釋或執行位元組碼。JIT編譯器在許多部分，然後快取並在以後重用，無需重新編譯。相比之下，傳統的解釋型虛擬機器只解釋位元組碼，通常效能要低得多。有些直譯器甚至不需要首先編譯成位元組碼就可以解釋原始碼，但效能更差。靜態編譯的程式碼或原生代碼在部署之前編譯。動態編譯環境是在執行期間可以使用編譯器的環境。使用JIT技術的一個共同目標是達到或超過靜態編譯的效能，同時保持位元組碼解釋的優勢：解析原始原始碼和執行基本最佳化的許多“繁重工作”通常是在編譯時處理的，在部署之前：從位元組碼編譯到機器碼要比從原始碼編譯快得多。與原生代碼不同，部署的位元組碼是可移植的。由於執行時可以控制編譯，比如解釋位元組碼，所以它可以在安全的沙箱中執行。從位元組碼到機器碼的編譯器更容易編寫，因為行動式位元組碼編譯器已經完成了大部分工作。JIT程式碼通常比直譯器效能更好。另外，在某些情況下，它的效能可以比靜態編譯更好，因為許多最佳化只在執行時可行：由於JIT必須在執行時呈現和執行本地二進位制映像，因此真正的機器程式碼JIT需要允許在執行時執行資料的平臺，這使得在基於哈佛結構的機器上使用這種JIT成為不可能的事情——對於某些作業系統和虛擬機器也是如此。然而，一種特殊型別的“JIT”可能並不針對物理機器的CPU體系結構，而是一種最佳化的VM位元組碼，在這種情況下，對原始機器程式碼的限制佔了上風，特別是在位元組碼的VM最終將JIT用於本機程式碼的情況下。啟動延遲和最佳化.由於載入和編譯位元組碼所需的時間，JIT在應用程式的初始執行中會導致輕微到明顯的延遲。有時這種延遲被稱為“啟動時間延遲”或“預熱時間”。一般來說，JIT執行的最佳化越多，生成的程式碼就越好，但是初始延遲也會增加。因此，JIT編譯器必須在編譯時間和希望生成的程式碼質量之間進行權衡。除了JIT編譯之外，IO繫結操作也會增加啟動時間：例如，JVM的“rt.jar”類資料檔案為40 MB，JVM必須在這個巨大的上下文檔案中尋找大量資料。Sun的HotSpot Java虛擬機器使用的一種可能的最佳化方法是將解釋和JIT編譯結合起來。應用程式程式碼最初是被解釋的，但JVM監視哪些位元組碼序列經常被執行，並將它們轉換為機器程式碼，以便在硬體上直接執行。對於只執行幾次的位元組碼，這節省了編譯時間並減少了初始延遲；對於頻繁執行的位元組碼，JIT編譯用於在緩慢解釋的初始階段之後以高速執行。此外，由於程式花費大量時間執行的其實只是一小部分程式碼，因此減少的編譯時間非常重要。最後，在初始程式碼解釋期間，可以在編譯之前收集執行統計資訊，這有助於執行更好的最佳化。正確的權衡可以根據具體情況而變化。例如，Sun的Java虛擬機器有兩種主要模式: 客戶機和伺服器。在客戶端模式下，執行最小程度的編譯和最佳化，以減少啟動時間。在伺服器模式下，將執行大量的編譯和最佳化，以犧牲啟動時間來最大限度地提高應用程式執行時的效能。其他Java即時編譯器使用一個方法執行次數的執行時度量，結合方法的位元組碼大小作為一種啟發式方法來決定何時編譯。還有的使用執行的次數與檢測迴圈相結合。一般來說，在短期執行的應用程式中準確預測要最佳化的方法要比在長期執行的應用程式中準確得多。微軟的在極端情況下不如JIT編譯的程式碼的原因相同：缺乏分析資料來驅動，例如，內聯快取。還有一些Java實現將AOT編譯器與JIT編譯器結合起來。歷史.最早釋出的JIT編譯器通常歸功於約翰·麥卡錫在1960年對LISP的研究。在他的重要論文《符號表達式的遞迴函式及其在機器上的計算》第一部分中，他提到了在執行時被轉換的函式，因此不需要儲存編譯器輸出來打孔卡。另一個早期的應用來自肯·湯普遜，他在文字編輯器QED的正則表示式模式匹配中使用了JIT。為了提高速度，Thompson在相容分時系統上透過JIT到IBM 7090程式碼實現了正則表示式匹配。1970年，Mitchell首創了一種有影響力的從解釋中獲取編譯程式碼的技術，他在實驗語言LC²中實現了這種技術。Smalltalk開創了JIT編譯的新領域。例如，按需翻譯為機器程式碼，快取結果以供以後使用。當記憶體不足時，系統會刪除部分程式碼，並在需要時重新生成。Sun的Self語言廣泛地改進了這些技術，一度是世界上速度最快的Smalltalk系統；運用完全面向物件的語言實現了高達最佳化C語言一半的速度。Self被Sun拋棄了，但是研究轉向了Java語言。“即時編譯”這個術語是從製造術語“及時”中借來的，並由Java普及，James Gosling從1993年開始使用這個術語。目前，大多數Java虛擬機器的實現都使用JIT技術，因為HotSpot建立在這個研究基礎之上，而且使用廣泛。HP的專案Dynamo是一個實驗性的JIT編譯器，其位元組碼格式和機器程式碼格式是相同的；該系統將PA-6000機器程式碼轉換為PA-8000機器程式碼。與直覺相反，這導致了速度的提高，在某些情況下是30%，因為這樣做允許在機器程式碼級別進行最佳化，例如，內聯程式碼以更好地使用快取，最佳化對動態庫的呼叫，以及許多其他常規編譯器無法嘗試的執行時最佳化。2019年3月30日，PHP宣佈JIT將於2021年加入PHP 8。安全.JIT編譯從根本上使用可執行資料，因此帶來了安全挑戰和可能的漏洞。JIT編譯的實現包括將原始碼或位元組碼編譯成機器碼並執行它。這通常是直接在記憶體中完成的——JIT編譯器將機器程式碼直接輸出到記憶體中並立即執行，而不是像通常的提前編譯那樣將其輸出到磁碟，然後作為單獨的程式呼叫程式碼。在現代的體系結構中，由於，這會遇到一個問題——無法在任意記憶體裡執行程式。在任意記憶體裡執行程式存在潛在的安全漏洞。因此，必須將記憶體標記為可執行；出於安全原因，應在程式碼寫入記憶體並標記為只讀之後執行，因為可寫/可執行記憶體是一個安全漏洞。例如Firefox的JavaScript的JIT編譯器在Firefox 46版本中引入了這種保護。是一種利用漏洞利用的技術，它使用JIT編譯進行——生成的記憶體然後是可執行的，如果執行可以移動到堆積中，這就允許利用。