项目地址:
Jvm在启动一个程序的时候,会涉及到非常多的内容,其中就有一些JNI方法。拿 System.out 来说,这个变量是 final 修饰的,按照一般情况来说,我们是无法更改这个变量的值的:
但是 System 类提供了一个 setOut 方法:
可以看出是使用了 native 方法来实现变量的赋值。
我们之前一直是使用 hack 的方式来打印输出:
如果想真正的实现 System.out.println 方法,我们就需要实现这些JNI方法。

本地方法实现

本地方法在class文件中没有Code属性,所以需要主动给maxStack和maxLocals字段赋值。
本地方法帧的操作数栈至少要能容纳返回值,为了简化代码,暂时给maxStack字段赋值为4。
因为本地方法帧的局部变量表只用来存放参数值,所以把argSlotCount赋给maxLocals字段刚好。
至于code字段,也就是本地方法的字节码,它本是没有字节的,但是我们需要模拟本地方法的调用,所以就自己设定一个操作符 0xFE,它表示 invokenative,返回指令则根据函数的返回值选择相应的返回指令。
我们在创建Method的时候,判断:
injectCodeAttribute 里面就加入两条指令:
这样我们的 NativeMethod 就创建好了,但是还有一个问题,就是这个 code 里面没有逻辑,我们该如何实现该方法的具体逻辑呢?
本地方法的实现只能靠自己去翻译JNI代码。拿 System 类来说,它的 static 代码段里面就有一个 native 方法:
我们实现一个本地方法注册表:
注册的时候需要传递一个实现了 NativeMethod 接口的类,这个类里面就是具体的实现逻辑:
当执行到 InvokeNative 指令的时候,我们根据方法签名去匹配本地方法:
这样,本地方法就搞定了。
但是很蛋疼的是,JVM在启动的时候会调用到很多的本地方法。所以实现起来特别的累,到现在项目里面还有很多本地方法没有实现,后面才会慢慢补上。不过话说回来,实现这些本地方法对理解项目也没多少帮助是,属于吃力不讨好。

反射实现

前面在实现运行时数据区的时候,创建了两个类:MyClass 和 MyObject。
对于 String 的字节码文件来说,MyClass 表示的是String.class。MyObject 表示的是一个 String 对象。
但是对于 Class 的字节码来说,MyClass 表是的是 Class.class 。 MyObject 表示的是 String.class 或者其他的 clas。
notion image
嗯,可能比较难理解。没关系,多看几遍就自然懂了。
为了记录 MyObject 与 MyClass 的关系,我们使用 extra 字段来建立联系。
每次MyClassLoader加载类的时候,我们都建立如下联系:
这样,就可以正确的运行 String.class 这样的代码了。
 

代码测试

运行配置:
notion image
运行结果:
notion image
 
Loading...
二手的程序员
二手的程序员
路漫漫其修远兮
公告
 
可以占个坑,虽然现在没人说话。
QQ群:
notion image
Telegram群:
notion image
 
我的公众号:
notion image
我的微信:
notion image