您的位置: 首页 > 编程笔记

什么是 JIT 即时编译

Java JIT 即时编译 时间:2019-06-15  查看:28   收藏

名如其特点,JIT —— just in time,即时编译。

什么?这就是你要告诉大家伙的?这不是人人都知道的嘛?而且网上一搜也全都是 JIT = just in time 了事。好吧好吧,我知错啦。那就认真的定义一下JIT:

一个程序在它运行的时候创建并且运行了全新的代码,而并非那些最初作为这个程序的一部分保存在硬盘上的固有的代码。就叫 JIT。

几个点:

  1. 程序需要运行

  2. 生成的代码是新的代码,并非作为原始程序的一部分被存在磁盘上的那些代码

  3. 不光生成代码,还要运行。

需要提醒的是第三点,也就是 JIT不光是生成新的代码,它还会运行新生成的代码。

模拟一下JIT的过程

JIT这么好,那它是如何实现既生成新代码,又能运行新代码的呢?

编译器如何生成代码很多文章都有涉及,我就不多在此着墨了。下面我就着重和各位聊聊,如何运行新生成的代码。

首先我们要知道生成的所谓机器码到底是神马东西。一行看上去只是处理几个数字的代码,蕴含着的就是机器码。

unsigned char[] macCode = {0x48, 0x8b, 0x07};

macCode对应的汇编指令就是:

mov    (%rdi),%rax

其实可以看出机器码就是比特流,所以将它加载进内存并不困难。而问题是应该如何执行。

好啦。下面我们就模拟一下执行新生成的机器码的过程。假设JIT已经为我们编译出了新的机器码,是一个求和函数的机器码:

//求和函数long add(long num) {   return num + 1; }  

//对应的机器码0x48, 0x83, 0xc0, 0x01, 0xc3

首先,动态的在内存上创建函数之前,我们需要在内存上分配空间。具体到模拟动态创建函数,其实就是将对应的机器码映射到内存空间中。这里我们使用c语言做实验,利用 mmap函数 来实现这一点。

头文件:
#include <unistd.h> #include <sys/mman.h>
定义函数:
void *mmap(void *start, size_t length, int prot, int flags, int fd, off_t offsize)
函数说明:
mmap()用来将某个文件内容映射到内存中,对该内存区域的存取即是直接对该文件内容的读写。

因为我们想要把已经是 比特流的“求和函数”在内存中创建出来,同时还要运行它。所以mmap有几个参数需要注意一下。

代表映射区域的保护方式,有下列组合:

  • PROT_EXEC 映射区域可被执行;

  • PROT_READ 映射区域可被读取;

  • PROT_WRITE 映射区域可被写入;

#include<stdio.h>                                                                                            #include <stdlib.h>#include <string.h>#include <unistd.h>#include <sys/mman.h>//分配内存void* create_space(size_t size) {    void* ptr = mmap(0, size,
            PROT_READ | PROT_WRITE | PROT_EXEC,
            MAP_PRIVATE | MAP_ANON,            -1, 0);   
    return ptr;
}

这样我们就获得了一块分配给我们存放代码的空间。下一步就是实现一个方法将机器码,也就是比特流拷贝到分配给我们的那块空间上去。使用 memcpy 即可。

//在内存中创建函数void copy_code_2_space(unsigned char* m) {    unsigned char macCode[] = {        0x48, 0x83, 0xc0, 0x01,
        c3 
    };    memcpy(m, macCode, sizeof(macCode));
}

然后我们在写一个main函数来处理整个逻辑:

#include<stdio.h>                                                                                            #include <stdlib.h>#include <string.h>#include <unistd.h>#include <sys/mman.h>//分配内存void* create_space(size_t size) {    void* ptr = mmap(0, size,
            PROT_READ | PROT_WRITE | PROT_EXEC,
            MAP_PRIVATE | MAP_ANON,            -1, 0);   
    return ptr;
}//在内存中创建函数void copy_code_2_space(unsigned char* addr) {    unsigned char macCode[] = {        0x48, 0x83, 0xc0, 0x01,        0xc3 
    };    memcpy(addr, macCode, sizeof(macCode));
}//main 声明一个函数指针TestFun用来指向我们的求和函数在内存中的地址int main(int argc, char** argv) {                                                                                              
    const size_t SIZE = 1024;    typedef long (*TestFun)(long);    void* addr = create_space(SIZE);
    copy_code_2_space(addr);
    TestFun test = addr;    int result = test(1);    printf("result = %dn", result); 
    return 0;
}

编译运行一下看下结果:

//编译gcc testFun.c//运行./a.out 1

 

0% (0)
0% (0)
0.081958s