0%

JIT 入门

[TOC]

JIT概述

JIT是“Just In Time”的首字母缩写。每当一个程序在运行时创建并运行一些新的可执行代码,而这些代码在存储于磁盘上时不属于该程序的一部分,它就是一个JIT。

我认为JIT技术在分为两个不同的阶段时更容易解释:

阶段1:在程序运行时创建机器代码。

阶段2:在程序运行时也执行该机器代码。

第1阶段是JITing 99%的挑战所在,但它也是这个过程中不那么神秘的部分,因为这正是编译器所做的。众所周知的编译器,如gcc和clang,将C/C++源代码转换为机器代码。机器代码被发送到输出流中,但它很可能只保存在内存中(实际上,gcc和clang / llvm都有构建块用于将代码保存在内存中以便执行JIT)。第2阶段是我想在本文中关注的内容。

stackoverflow

A JIT compiler runs after the program has started and compiles the code (usually bytecode or some kind of VM instructions) on the fly (or just-in-time, as it’s called) into a form that’s usually faster, typically the host CPU’s native instruction set. A JIT has access to dynamic runtime information whereas a standard compiler doesn’t and can make better optimizations like inlining functions that are used frequently.

This is in contrast to a traditional compiler that compiles all the code to machine language before the program is first run.

To paraphrase, conventional compilers build the whole program as an EXE file BEFORE the first time you run it. For newer style programs, an assembly is generated with pseudocode (p-code). Only AFTER you execute the program on the OS (e.g., by double-clicking on its icon) will the (JIT) compiler kick in and generate machine code (m-code) that the Intel-based processor or whatever will understand.

JIT实现

demo

  1. 使用mmap在堆上分配可读,可写和可执行的内存块。
  2. 将实现add4函数的汇编/机器代码复制到此内存块中。
  3. 将该内存块首地址转换为函数指针,并通过调用这一函数指针来执行此内存块中的代码。

安全

内存块首先被分配了RW权限,因为我们需要将函数的机器代码写入该内存块。然后我们使用mprotect将块的权限从RW更改为RX,使其可执行但不再可写,所以最终效果是一样的,但是在我们的程序执行过程中,没有任何一个时间点,该内存块是同时可写的和可执行的。

本文介绍的这种技术几乎是真正的JIT引擎(例如LLVM和libjit)从内存中发出和运行可执行机器代码的方式,剩下的只是从其他东西合成机器代码的问题。LLVM有一个完整的编译器,所以它实际上可以在运行时将C和C ++代码(通过LLVM IR)转换为机器码,然后执行它。

JIT 的优势

  1. 编译耗时 有的需求是需要考虑程序的启动时间的
  2. 2.在运行期间收集一些数据可以更好的优化原本的代码(激进优化) 这也是JIT的优势之一
  3. 3.Java本身就支持一开始就本地编译…所以看你的选择

参考

JIT原理 - 简介

使用 Go 语言写一个即时编译器(JIT)