《自己动手写Java虚拟机》学习笔记(九)本地方法调用

第九章 本地方法调用

想要运行Java程序,出了Java虚拟机外,还需要Java类库的配合。Java虚拟机和Java类库一起构成了Java运行环境。Java类库主要用Java语言编写,一些无法用Java语言实现的方法则使用本地语言编写,这些方法叫做本地方法。

9.1注册和查找本地方法

在开始实现本地方法之前,需要先实现一个本地方法注册表,用来注册和查找本地方法。

把本地方法定义成为一个函数,参数是Frame结构体指针,没有返回值。这个Frame参数就是本地方法的工作空间,也就是连接Java虚拟机和Java类库的桥梁。

我们用一个map来管理本地方法。值是具体的本地方法实现,键是类名、方法名、方法描述符的组合。

java.lang.Object等类是通过一个叫做registerNatives()的本地方法来注册其他本地方法的。

9.2 调用本地方法

JVM规范并没有规定如何实现和调用本地方法。这里我们利用Java虚拟机栈执行本地方法。

但是本地方法没有字节码。幸好Java虚拟机规范预留了两条指令,操作码分别是0xFE和0xFF。

对于任意一个方法,如果它是本地方法,则注入字节码和其他信息。因为本地方法在class文件中没有Code属性,所以需要为其设定操作数栈和局部变量表的空间。其中,本地方法的局部操作数栈至少要能够容纳返回值。本地方法帧的局部变量表用来存放参数值。至于Code,我们设定其第一条指令为0xFE,第二条指令根据函数返回值选择对应的返回指令。

根据类名,方法名和方法描述符从本地方法注册表中查找到本地方法。如果找不到,抛异常。找了就调用。

9.3 反射

类和对象之间的关系。如图:

 

我们用jClass表示该类的类对象。用extra表示一个实例的额外信息。heap中Object1、Object2都是类对象,其中Object1是java.lang.Object的类对象,Object2是java.lang.Class的类对象。Object3是java.lang.Object的实例对象。而在方法区中,都是加载的类。

为了实现上图,我们需要修改类加载器和Object结构体,让每一个加载到方法去中类都有一个类对象与之相关联。首先加载java.lang.Class类,这又会触发java.lang.Object和其接口的加载。然后遍历已经加载的所有类,给他们每一个类关联类对象。

关于基本类型的类。和数组一样,基本类型的类也是由java虚拟机在运行期间生成的。这里有三点要说明:①voide和基本类型的类名就是void,int,float等;②基本类型的类没有超类,也没有实现任何接口;③非基本类型的类对象是通过ldc指令加载到操作数栈中的;而基本对象是通过getstatic指令。每一个基本类型都有一个包装类,包装类中有一个静态常量叫TYPE,其中存放的就是基本类型的类。也就是说,基本类型的类是通过getstatic指令访问相应的包装类的TYPE字段加载到操作数栈中的。

通过反射获取类名。java.lang.Object.getClass()方法的实现:首先从局部变量表中拿到this引用,然后得到其Class,进而通过jClass得到类对象,再把类对象推入操作数栈顶。java.lang.Object.getPrimitiveClass()方法实现:先从局部变量表得到类名。由于基本类型的类已经加在到了方法区,直接获取即可,最后把类对象引用推入操作数栈顶。java.lang.Object.getName0()方法的实现:首先从局部变量表中得到this引用。然后拿到类名,转化成为Java字符串并推入操作数栈顶。

9.4 字符串拼接和String.intern()方法

通过对StringBuilder.appen()和StringBuilder.toString()方法深度挖掘。发现其实落脚到了System.arrayCopy()、Float.floatToRawIntBits()、Double.doubleToRawLongBits()上。

System.arrayCopy()方法实现。其java定义为:

public static native void arrayCopy(Object src , int srcPos , Objectdest,
                int destpos , int length);

因此我们需要先从局部变量表拿出五个参数。如果源数组或目标数组是null,抛异常。如果源数组和目标数组不兼容,抛异常。只有参数合法才能进行拷贝。

Float.floatToRawIntBits()、Double.doubleToRawLongBits()大同小异。可以直接用Go语言math包提供方法将float转化为bits。

String.intern()方法。如果字符串还没如池,把它放入并返回该字符串,否则找到已入池的字符串并返回。

9.5 Object.hashCode()、equals()、toString()

hashCode()方法可直接使用Go语言报的方法,另外两个是java实现好的。

9.6Object.clone()

如果类没有实现Cloneable接口,抛异常,否则开始克隆对象。把对象引用推入操作数栈顶进行数据克隆。

9.7 自动装箱和拆箱

这不是jvm的调整,这种增强完全是由编译器完成的。

通过分析Integer.Valueof()方法,发现他并不是每次都创建Integer对象,而是维护了一个缓存池,对于比较小的int变量,在池初始化时就预先加载到了池中,需要时直接从池中获取

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值