JNI
2024-12-01 08:57:40 0 举报
AI智能生成
JNI (Java Native Interface) 是一种允许Java代码和其他编程语言(尤其是C和C++)进行交互的机制。它允许Java程序员在Java代码中调用本地方法,这些方法通常是用C或C++编写的。这种机制使得Java程序能够访问操作系统的底层功能,例如文件I/O、图形处理和网络通信。JNI是Java平台不可或缺的一部分,因为它提供了与各种底层系统和硬件进行交互的能力。
作者其他创作
大纲/内容
JNI如何实现Java与C++互通
JNI是Java Native Interface的缩写
Java可以通过JNI作为桥梁实现Java程序与C语言、C++编写的程序互调
Java可以通过JNI作为桥梁实现Java程序与C语言、C++编写的程序互调
什么场景下会用到JNI
1.用Java实现不了或者实现起来比较复杂。比如人脸识别、图像识别
2.驱动硬件
1.用Java实现不了或者实现起来比较复杂。比如人脸识别、图像识别
2.驱动硬件
什么是动态链接库?
动态链接库:Linux平台下以【.so】结尾、Windows平台以下【.dll】结尾。程序编译时不会打包进可执行文件。
静态链接库:程序编译时会打包进可执行文件.相比动态链接库,生成的可执行文件更大。但是可独立运行
动态链接库:Linux平台下以【.so】结尾、Windows平台以下【.dll】结尾。程序编译时不会打包进可执行文件。
静态链接库:程序编译时会打包进可执行文件.相比动态链接库,生成的可执行文件更大。但是可独立运行
如何生成?
C语言用gcc编译,C++用g++编译
编译的时候,不需要放.h文件
C语言用gcc编译,C++用g++编译
编译的时候,不需要放.h文件
C语言
/usr/bin/gcc -dynamiclib -l /Library/JavaVirtualMachines/openjdk-12.-.2.jdk/Contents/Home/include source/com_ziya_jni_TestPrint.cpp -o /lib/libjni.so
/usr/bin/gcc -dynamiclib -l /Library/JavaVirtualMachines/openjdk-12.-.2.jdk/Contents/Home/include source/com_ziya_jni_TestPrint.cpp -o /lib/libjni.so
C++
/usr/bin/g++ -dynamiclib -l /Library/JavaVirtualMachines/openjdk-12.-.2.jdk/Contents/Home/include source/com_ziya_jni_TestPrint.cpp -o /lib/libjni.so
/usr/bin/g++ -dynamiclib -l /Library/JavaVirtualMachines/openjdk-12.-.2.jdk/Contents/Home/include source/com_ziya_jni_TestPrint.cpp -o /lib/libjni.so
生成的动态链接库放哪儿Java程序能找到
System.out.println(System.getProperty("java.library.path"));
System.out.println(System.getProperty("java.library.path"));
如何使用
文件名格式:lib+文件名+".so",真正在用的时候写文件名就行了。比如libhello.so
System.loadLibraray("hello");
文件名格式:lib+文件名+".so",真正在用的时候写文件名就行了。比如libhello.so
System.loadLibraray("hello");
JNI头文件
如何生成
javac [-encoding utf8] -h targetDir sourceFile
eg:javac -h /home/ziya/IdeaProjects/java-research/jni /home/ziya/IdeaProjects/java-research/src/main/java/com/luban/jni/MyJniTest.java
javac [-encoding utf8] -h targetDir sourceFile
eg:javac -h /home/ziya/IdeaProjects/java-research/jni /home/ziya/IdeaProjects/java-research/src/main/java/com/luban/jni/MyJniTest.java
IDE配置external tool生成
$FileDirRelativeToProjectRoot$/$FileName$
-h
./jni
$FileDirRelativeToProjectRoot$/$FileName$
-h
./jni
注意
1.只要Java文件中有native方法才会生成JNI头文件
2.如果Java程序中引用了其他的类,比如extends,需要关联引用的文件才能生成JNI头文件
1.只要Java文件中有native方法才会生成JNI头文件
2.如果Java程序中引用了其他的类,比如extends,需要关联引用的文件才能生成JNI头文件
函数原型解释
如果是静态方法,第二个参数是jclass类型。如果是非静态方法,第二个参数是jobject类型,即this.
如果是静态方法,第二个参数是jclass类型。如果是非静态方法,第二个参数是jobject类型,即this.
handle模型
在oop模型、klass模型外面又包装了一层。
JNI_ENTRY(jobject, jni_NewGlobalRef(JNIEnv *env, jobject ref))
JNIWrapper("NewGlobalRef");
Handle ref_handle(thread, JNIHandles::resolve(ref));
jobject ret = JNIHandles::make_global(ref_handle);
return ret;
JNI_END
在oop模型、klass模型外面又包装了一层。
JNI_ENTRY(jobject, jni_NewGlobalRef(JNIEnv *env, jobject ref))
JNIWrapper("NewGlobalRef");
Handle ref_handle(thread, JNIHandles::resolve(ref));
jobject ret = JNIHandles::make_global(ref_handle);
return ret;
JNI_END
开发JNI程序
需要配置头文件所在路径
include_directories("/home/ziya/Documents/openjdk/build/linux-x86_64-normal-server-slowdebug/jdk/include/")
include_directories("/home/ziya/Documents/openjdk/build/linux-x86_64-normal-server-slowdebug/jdk/include/linux")
include_directories("/home/ziya/Documents/openjdk/build/linux-x86_64-normal-server-slowdebug/jdk/include/")
include_directories("/home/ziya/Documents/openjdk/build/linux-x86_64-normal-server-slowdebug/jdk/include/linux")
JNI相关API所在位置(jni.h)
可以以C语言的方式调用,也可以以C++的方式调用,略有不同
可以以C语言的方式调用,也可以以C++的方式调用,略有不同
C语言端
生成.so文件
/usr/bin/gcc -dynamiclib -shared -fpic -I/home/ziya/Documents/openjdk/build/linux-x86_64-normal-server-slowdebug/jdk/include/ -I/home/ziya/Documents/openjdk/build/linux-x86_64-normal-server-slowdebug/jdk/include/linux com_luban_jni_MyJniTest.c -o /lib/libjni_my.so
如果遇到jni_md.h文件找不到,需要将编译好的openjdk目录中的jni_md.h拷贝到它的上一层
jni_md.h文件找不到,需要将/home/ziya/Documents/openjdk/build/linux-x86_64-normal-server-slowdebug/jdk/include/linux下的jni_md.h
进入到上面的目录中执行cp jni_md.h ../即可
CLion的external tool配置
Program: /usr/bin/gcc
Arguments:
-shared
-fpic
-I/home/ziya/Documents/openjdk/build/linux-x86_64-normal-server-slowdebug/jdk/include/
com_qimingnan_jni_JniTest.c
-o
/lib/libjni.so
Working Directory: $ProjectFileDir$
生成.so文件
/usr/bin/gcc -dynamiclib -shared -fpic -I/home/ziya/Documents/openjdk/build/linux-x86_64-normal-server-slowdebug/jdk/include/ -I/home/ziya/Documents/openjdk/build/linux-x86_64-normal-server-slowdebug/jdk/include/linux com_luban_jni_MyJniTest.c -o /lib/libjni_my.so
如果遇到jni_md.h文件找不到,需要将编译好的openjdk目录中的jni_md.h拷贝到它的上一层
jni_md.h文件找不到,需要将/home/ziya/Documents/openjdk/build/linux-x86_64-normal-server-slowdebug/jdk/include/linux下的jni_md.h
进入到上面的目录中执行cp jni_md.h ../即可
CLion的external tool配置
Program: /usr/bin/gcc
Arguments:
-shared
-fpic
-I/home/ziya/Documents/openjdk/build/linux-x86_64-normal-server-slowdebug/jdk/include/
com_qimingnan_jni_JniTest.c
-o
/lib/libjni.so
Working Directory: $ProjectFileDir$
java端
生成.h头文件
javac -h /home/ziya/IdeaProjects/java-research/jni /home/ziya/IdeaProjects/java-research/src/main/java/com/luban/jni/MyJniTest.java
生成.h头文件
javac -h /home/ziya/IdeaProjects/java-research/jni /home/ziya/IdeaProjects/java-research/src/main/java/com/luban/jni/MyJniTest.java
使用JNI将内存池接入
一般来说,大家接触到的GC收集器的所有名词都是一个独立的实体:内存管理器、垃圾分配策略、
屏障、信息收集器、记忆集、卡表......我们这里只聚焦于内存管理器、垃圾分配策略。
屏障、信息收集器、记忆集、卡表......我们这里只聚焦于内存管理器、垃圾分配策略。
1.PS
内存管理器:ParalelScavengeHeap
垃圾回收策略:PSParallelCompact、PSMarkSweep(ParallelScavengeHeap::do_full_collection)
自调节控制器:PSAdaptiveSizePolicy
大对象判断如图
内存管理器:ParalelScavengeHeap
垃圾回收策略:PSParallelCompact、PSMarkSweep(ParallelScavengeHeap::do_full_collection)
自调节控制器:PSAdaptiveSizePolicy
大对象判断如图
2.Serial(我们的JVM参考这个去实现)
内存管理器:GenCollectedHeap
垃圾回收策略:MarkSweepPolicy(Serial)、ASConcurrentMarkSweepPolicy与ConcurrentMarakSweepPolicy(CMS)
内存管理器:GenCollectedHeap
垃圾回收策略:MarkSweepPolicy(Serial)、ASConcurrentMarkSweepPolicy与ConcurrentMarakSweepPolicy(CMS)
3.G1
内存管理器:G1CollectedHeap
垃圾回收策略:G1CollectorPolicyExt
内存管理器:G1CollectedHeap
垃圾回收策略:G1CollectorPolicyExt
JVM的堆区是何时如何创建的
注意理解一句话:不是由GC算法决定内存模型,而是希望支持什么样的GC算法,需要设计什么样的内存模型
注意理解一句话:不是由GC算法决定内存模型,而是希望支持什么样的GC算法,需要设计什么样的内存模型
根据不同的垃圾回收策略去创建不同的内存模型
当确定好Universe::_collectedHeap(垃圾回收策略之后),接着会调用initialize()
以G1收集器为例,接着就是要向操作系统申请内存。具体实现在reserve_heap中
reserve_heap方法,最终一定会调用到os目录中的代码
接入思路
以永久代的方式存储klass模型
1.模拟启动JVM初始化堆
2.重写类解析器,将klass模型写入永久代
3.模拟创建对象,将oop模型写入新生代
以永久代的方式存储klass模型
1.模拟启动JVM初始化堆
2.重写类解析器,将klass模型写入永久代
3.模拟创建对象,将oop模型写入新生代
Unsafe.allocateMemory()
C++中new一个对象其实是在一个进程的堆中创建的,HotSpot要想new在自己的堆中,肯定要重写new,重写了new,对应的delete方法也要重写
C++中new一个对象其实是在一个进程的堆中创建的,HotSpot要想new在自己的堆中,肯定要重写new,重写了new,对应的delete方法也要重写
自实现OOP机制
OOP作为Java语言的基石,所有的特性都需要此基石支撑。
JVM的三种模型
1.oop模型: InstanceOop
2.Klass模型 InstanceKlass InstanceMirrorKlass
3.handle模型 jint(int类型做了一层包装)
JVM的三种模型
1.oop模型: InstanceOop
2.Klass模型 InstanceKlass InstanceMirrorKlass
3.handle模型 jint(int类型做了一层包装)
JVM实现思路
1.用C++从0开始,自己实现所有
2.用C++从0开始,jdk部分用openjdk的,比如System类
3.用JNI作为桥梁,一点一点的接入。比如内存池。openJDK有的通过JNI调用,比如System
1.用C++从0开始,自己实现所有
2.用C++从0开始,jdk部分用openjdk的,比如System类
3.用JNI作为桥梁,一点一点的接入。比如内存池。openJDK有的通过JNI调用,比如System
OOP实现思路
1.传统方式(先以这种方式实现)
2.内存编织方式(进阶实现)
1.传统方式(先以这种方式实现)
2.内存编织方式(进阶实现)
klass状态
enum Classstate {
allocated, // allocated (but not yet linked)
loaded, // loaded and inserted in class hierarchy (but not linked yet)
linked, // successfully linked/verfied (but not initialized yet)
being_initialized, // currently running class initializer
fully_initialized, // initialied (successfull final state)
initialization_error, // error happened during initialization
}
allocated状态表示已经分配内存,在InstanceKlass的构造函数中通常会将_init_state初始化为这个状态,
loaded状态表示类已经装载并且已经插入到继承体系中,在SystemDictionary:add_to_hierarchy()方法中会更新
linked状态标识已经成功连接/校验,只在InstanceKlass::link_class_impl()方法中更新为这个状态
另外3个状态是在类的初始化方法InstanceKlass::initialize_impl()中使用
初始化之前,会执行<clinit>静态代码块,初始化之后会上锁,保证线程安全
1.klass-oop-handle体系,还要实现mirrorKlass
2.用C++重写类解析器,生成Java类对应的Klass对象、mirrorKlass对象
3.实现方法初始化
4.实现对象创建(无继承无实现)
5.实现属性读写(静态、非静态)
6.实现方法调用(重复调用、嵌套调用)
7.实现继承(无属性无方法)
8.实现属性继承、属性读写(非多态访问、多态访问)
9.属性方法继承、方法访问(非多态访问、多态访问)
10.接口实现
11.多态访问
enum Classstate {
allocated, // allocated (but not yet linked)
loaded, // loaded and inserted in class hierarchy (but not linked yet)
linked, // successfully linked/verfied (but not initialized yet)
being_initialized, // currently running class initializer
fully_initialized, // initialied (successfull final state)
initialization_error, // error happened during initialization
}
allocated状态表示已经分配内存,在InstanceKlass的构造函数中通常会将_init_state初始化为这个状态,
loaded状态表示类已经装载并且已经插入到继承体系中,在SystemDictionary:add_to_hierarchy()方法中会更新
linked状态标识已经成功连接/校验,只在InstanceKlass::link_class_impl()方法中更新为这个状态
另外3个状态是在类的初始化方法InstanceKlass::initialize_impl()中使用
初始化之前,会执行<clinit>静态代码块,初始化之后会上锁,保证线程安全
1.klass-oop-handle体系,还要实现mirrorKlass
2.用C++重写类解析器,生成Java类对应的Klass对象、mirrorKlass对象
3.实现方法初始化
4.实现对象创建(无继承无实现)
5.实现属性读写(静态、非静态)
6.实现方法调用(重复调用、嵌套调用)
7.实现继承(无属性无方法)
8.实现属性继承、属性读写(非多态访问、多态访问)
9.属性方法继承、方法访问(非多态访问、多态访问)
10.接口实现
11.多态访问
封装
继承
多态
一个类有属性和方法,可以继承和实现其他类。
属性分为静态和非静态
new指令会触发父类的加载
new一个对象对应的四条字节码指令
new 创建一个空对象,内存地址入栈
dup 复制栈顶元素,接下来要调用init非静态方法,但this指针还是空的,所以需要把栈顶元素弹出去,给this指针赋值,然后再把元素压入栈
invokespecial <init> 调用init方法
astore_1 pop出元素,赋值给index=1的局部变量表
加载阶段
SytemDictionary::resolve_or_null -> parseClassFile
link阶段的触发是在合适的阶段被触发的,例如new一个对象时 link_class.
HotSpot源码中是有验证阶段和解析阶段的,但是没有准备阶段(为类变量分配内存,设置默认值。但是在到达初始化。之前,类变量都没有初始化为真正的初始值)
多态
invokevirtual 类继承相关
invokeinterface 接口实现相关
vtable dispatch 虚表分发
继承
多态
一个类有属性和方法,可以继承和实现其他类。
属性分为静态和非静态
new指令会触发父类的加载
new一个对象对应的四条字节码指令
new 创建一个空对象,内存地址入栈
dup 复制栈顶元素,接下来要调用init非静态方法,但this指针还是空的,所以需要把栈顶元素弹出去,给this指针赋值,然后再把元素压入栈
invokespecial <init> 调用init方法
astore_1 pop出元素,赋值给index=1的局部变量表
加载阶段
SytemDictionary::resolve_or_null -> parseClassFile
link阶段的触发是在合适的阶段被触发的,例如new一个对象时 link_class.
HotSpot源码中是有验证阶段和解析阶段的,但是没有准备阶段(为类变量分配内存,设置默认值。但是在到达初始化。之前,类变量都没有初始化为真正的初始值)
多态
invokevirtual 类继承相关
invokeinterface 接口实现相关
vtable dispatch 虚表分发
类的初始化initialized_impl
parseClassFile()->parseFields()->调整变量内存布局
parseClassFile()->parseFields()->调整变量内存布局
layout_fields(class_loader, &fac, &parsed_annotations, &info, CHECK_NULL);
三种内存编织规则
解析属性,将会创建short[length][6]
接着将它解析之后放入FieldInfo Map<String, Integer>map,字段会有一个签名
接着将它解析之后放入FieldInfo Map<String, Integer>map,字段会有一个签名
JVM是如何调用Java方法的
多态的体现及相关指令
1.类的继承 invokevirtual vtable
2.接口实现 invoke interface itable
Jvm中调用方法
invokestatic 调用静态方法
invokespecial private、构造方法
invokevirtual 多态方法,虚表分发 (vtable dispatch)
invokeinterface 调用接口实现
invokedynamic lambda表达式
1.类的继承 invokevirtual vtable
2.接口实现 invoke interface itable
Jvm中调用方法
invokestatic 调用静态方法
invokespecial private、构造方法
invokevirtual 多态方法,虚表分发 (vtable dispatch)
invokeinterface 调用接口实现
invokedynamic lambda表达式
如何实现
通过父类引用访问子类方法,底层是借助虚表实现的。如图,其底层就是用数组来存储额。越处于继承链的顶端,其中的方法越靠前存储。
在C++中,这张表叫虚表。在JVM中,这张表叫做vtable,位置在klass对象的后面。
只要是通过invokevirtual指令调用的方法都需要写入vtable.哪些方法通过invokevirtual指令调用呢?被protected、public修饰,且不被final、static修饰的方法。满足这些条件的方法又称为虚方法。那native方法要插入vtable吗? 要
写程序实现时,要先处理parent.parent的虚方法,再处理parent的虚方法,再处理自己的虚方法。具体细节:
1.因为Object类,自实现的JVM是不解析的,那顶层的父类的super klass是NULL,所以程序需要处理
a.super klass为空
b.super klass不为空
2.因为不知道继承链有多长,虚表递归处理
通过父类引用访问子类方法,底层是借助虚表实现的。如图,其底层就是用数组来存储额。越处于继承链的顶端,其中的方法越靠前存储。
在C++中,这张表叫虚表。在JVM中,这张表叫做vtable,位置在klass对象的后面。
只要是通过invokevirtual指令调用的方法都需要写入vtable.哪些方法通过invokevirtual指令调用呢?被protected、public修饰,且不被final、static修饰的方法。满足这些条件的方法又称为虚方法。那native方法要插入vtable吗? 要
写程序实现时,要先处理parent.parent的虚方法,再处理parent的虚方法,再处理自己的虚方法。具体细节:
1.因为Object类,自实现的JVM是不解析的,那顶层的父类的super klass是NULL,所以程序需要处理
a.super klass为空
b.super klass不为空
2.因为不知道继承链有多长,虚表递归处理
何时写入vtable? 可以有两个选择
1.解析字节码文件的时候
2.需要用到虚表的时候,即调用的时候。JVM是选择这个时机触发插入的。具体是link阶段
JVM在执行这些字节码指令之前进行link:anewarray、checkcast、getfield、getstatic、instanceof、invokedynamic、invokeinterface、invokespecial、invokestatic、invokevirtual、ldc、ldc_w、ldc2_w、multianewarray、new、putfield
1.解析字节码文件的时候
2.需要用到虚表的时候,即调用的时候。JVM是选择这个时机触发插入的。具体是link阶段
JVM在执行这些字节码指令之前进行link:anewarray、checkcast、getfield、getstatic、instanceof、invokedynamic、invokeinterface、invokespecial、invokestatic、invokevirtual、ldc、ldc_w、ldc2_w、multianewarray、new、putfield
如何调用?
按顺序找,调用最后出现的那个,这也就是为什么子类重写了父类中的方法,调用的是子类中的原因所在
按顺序找,调用最后出现的那个,这也就是为什么子类重写了父类中的方法,调用的是子类中的原因所在
模板解释器原理
1.call_stub
2.entry_point
1.call_stub
2.entry_point
执行字节码案例
Java代码
public class Test_1 {
public static void main(String[] args) {
int a = 10;
}
}
生成的字节码指令
0 bipush 10
2 istore_1
3 return
Java代码
public class Test_1 {
public static void main(String[] args) {
int a = 10;
}
}
生成的字节码指令
0 bipush 10
2 istore_1
3 return
1.第一步,找到main方法
再点进去你会发现,它是一个接口实现,并且还在jni.h当中,定位不到方法实现中
我们需要jni+下划线+方法名。到jni.cpp里面去找
2.调用jni_invoke_static
3.调用call_stub
call_stub生成的代码长什么样?
哪里生成的?stubGenerator_x86_64.cpp#generate_initial() -> generate_all_stub
call_stub生成的代码长什么样?
哪里生成的?stubGenerator_x86_64.cpp#generate_initial() -> generate_all_stub
StubRoutines::initialize1()
StubGenerator_generator
generate_call_stub里面的 __ enter()做的就是在开启堆栈
解决了什么问题?
虚拟机栈在哪里?main方法其实就是call_stub调的,,
需要准备调用环境,call_stub是一个函数指针,开辟堆栈
call_stub在调用main方法的时候,有两个步骤,第一个是生成call_stub,第二个执行entry_point,
然后才会调用到main方法当中去
call_stub做的事情
需要创建自己的栈也就是OS中的栈,然后再创建虚拟机栈。虚拟机栈式寄生在call_stub的栈中,为什么要这样做?
因为硬编码编织纪要支持push这样的汇编指令,又要支持虚拟机栈,很矛盾的点,又要支持push汇编指令又要用到虚拟机栈,怎么解决呢?
两种方案
1.寄生。虚拟机栈寄生在call_stub的栈中,在call_stub的下面拉伸一段,96字节
2.移花接木。 也是自实现的JVM版本中的方式,两个虚拟机栈,怎么来回切换呢?如果说在OS栈中,需要用到虚拟机栈,把rsp/esp,切到虚拟机栈,然后用完之后,再切回到OS栈中。切栈
虚拟机栈在哪里?main方法其实就是call_stub调的,,
需要准备调用环境,call_stub是一个函数指针,开辟堆栈
call_stub在调用main方法的时候,有两个步骤,第一个是生成call_stub,第二个执行entry_point,
然后才会调用到main方法当中去
call_stub做的事情
需要创建自己的栈也就是OS中的栈,然后再创建虚拟机栈。虚拟机栈式寄生在call_stub的栈中,为什么要这样做?
因为硬编码编织纪要支持push这样的汇编指令,又要支持虚拟机栈,很矛盾的点,又要支持push汇编指令又要用到虚拟机栈,怎么解决呢?
两种方案
1.寄生。虚拟机栈寄生在call_stub的栈中,在call_stub的下面拉伸一段,96字节
2.移花接木。 也是自实现的JVM版本中的方式,两个虚拟机栈,怎么来回切换呢?如果说在OS栈中,需要用到虚拟机栈,把rsp/esp,切到虚拟机栈,然后用完之后,再切回到OS栈中。切栈
call_stub做了两件事情
1.创建堆栈
2.进入entry_point
1.创建堆栈
2.进入entry_point
怎么进入的entry_point呢?
将Java方法放入到了rbx寄存器当中,然后执行
将Java方法放入到了rbx寄存器当中,然后执行
entry_point什么时候生成的呢?
JVM启动的时候,在init_globals()里面的时候生成->interpreter_init()->TemplateInterpreter::initialize()
JVM启动的时候,在init_globals()里面的时候生成->interpreter_init()->TemplateInterpreter::initialize()
普通的Java方法定义成zerolocals
method_entry(zeroloclas)
method_entry(zerolocals_synchronized) 带了synchronized修饰
method_entry(zeroloclas)
method_entry(zerolocals_synchronized) 带了synchronized修饰
4.method_entry(zerolocals)
Interpreter::_entry_table[Interpreter::kind] = generate_method_entry(Interpreter::kind);
return ig_this->generate_normal_entry(synchronized);
Interpreter::_entry_table[Interpreter::kind] = generate_method_entry(Interpreter::kind);
return ig_this->generate_normal_entry(synchronized);
return ig_this->generate_normal_entry(synchronized);
生成路径
interpreter_init()->
interpreter_init()->
Interpreter继承了CppInterpreter和TemplateInterpreter,我们点进TemplateInterpreter看它的initialize()方法
这里进行了TemplateTable初始化,以及InterpreterGenerator的构造方法
我们点进去看它在x86_64下的构造方法里面做了什么
调用了generate_all()
上面的代码点过去不知道为什么跳转到的式cppInterpreter,正确的式应该是跳到templateInterpreter.cpp
5.__ dispatch_next(vtos);
def(Bytecodes::_bipush , ubcp|____|____|____, vtos, itos, bipush , _ );
def(Bytecodes::_bipush , ubcp|____|____|____, vtos, itos, bipush , _ );
6.void TemplateTable::bipush() {
transition(vtos, itos);
// 将bipush后面的操作数赋值给rax
__ load_signed_byte(rax, at_bcp(1));
}
transition(vtos, itos);
// 将bipush后面的操作数赋值给rax
__ load_signed_byte(rax, at_bcp(1));
}
0 条评论
下一页
为你推荐
查看更多