《自己动手写Java虚拟机》学习笔记(六)

第六章 类和对象

本章实现线程共享的运行时数据区,包括方法区和运行时常量池。并且根据第三章对.class文件的解析,继续优化转换以放入方法区以供后续使用。

本章还有讨论类和对象的设计、实现一个简单类加载器、实现类和对象的部分指令(对第五章进行扩充)。

6.1 方法区

方法区主要从class文件获取类的信息。此外,类变量也存放在方法区里。到jvm第一次使用这个类的时候,它会搜索类路径,找到对应的class文件,然后读取并解析class文件,把相关的信息放入方法区。

我们需要用一个结构体Class对应方法区的一个类。(其实就是类比Java中的Class<?>类型)。

方法和字段都属于类成员,他们有一些相同的方法(抽象成为ClassMember)。但是方法信息中有字节码(类比Method和Filed)。两者都各自需要具体实现。

6.2 运行时常量池

运行时常量池主要存放两类信息:字面量和符号引用。字面量包括整数、浮点数和字符串字面量;符号引用包括类符号引用、字段符号引用、方法符号引用和接口方法引用。

他需要根据索引返回常量,和能把class文件中的常量池转化为运行时常量池。

对于类符号引用,只要有类名,就可以解析符号引用;对于字段,首先要解析类符号引用得到类数据,然后用字段名和描述符查找字段数据。方法符号引用解析过程和字段符号引用类似。

6.3 类加载器

类加载器依赖Classpath来搜索和读取class文件。

可以使用一个map当作方法区的具体实现。先查找map,看类是否已经被加载,如果是,直接返回类数据;否则调用其他方法加载类。注意,数组类和普通类不同,它的数据是由jvm运行期间生成,而不是来自class文件。

类加载三大步:首先找到class文件把数据读取到内存;解析class文件,生成虚拟机可用的类数据,放入方法区;最后进行链接。注意第二步:出了java.lang.Object都需要递归的加载父类。

6.4 对象、实例变量和类变量

可以用一个数组(或类似数组的数据类型)来表示类变量和实例变量。但是:

1.如何确定这个数组的空间大小呢?如果该类有m个静态字段,则真正需要的空间数至少和m一样大(因为继承关系)。实例变量亦是如此。

2.如何确定字段位置?编号!但是注意:静态字段和实例字段分开编号!实例字段要从继承关系最高成(也就是Object)开始编号,否则容易发生混乱。最后要考虑long和double类型。

6.5 类和字段符号引用

如果类D要通过符号引用N引用类C,要解析N,先用D的类加载器加载C,然后检查D是否有权限访问C。当C值public或者C和D用一个运行包内才有权限访问。否则要出现异常。

果类D要通过符号引用N引用类C的某个字段,首先解析符号引用得到类C,然后根据字段名和描述符查找字段。如果字段查找失败抛异常,如果查找成功,看D是否有权限访问该字段。对于查找过程:首先在类C字段中查找,如果找不到就在C的接口递归查找,如果还是找不到就在C的继承关系向上查抄,如果仍然找不到则失败。

6.6 类和对象相关指令

1.new指令:专门用来创建类实例。需要一个操作数作为索引值。从当前类的运行时常量池找到一个类符号引用。解析类符号引用,拿到类数据,创建对象并把对象引用推入栈顶。

2.putstatic给类的静态变量赋值。需要两个操作数:常量池索引和值(要从操作数栈中弹出)。通过索引可以从当前类的运行时常量池中找到字段符号引用,解析符号引用给他赋值。注意如果解析的字段是是实例字段则抛异常;如果是final则只能在初始化方法中赋值,否则抛异常。getstatic功能正好相反。

3.putfiled给实例变量赋值。需要三个操作数:常量池索引,变量值和字段引用。getfiled类似。

4.instanceof指令判断对象是否是某个类的实例(或者对象的类是否实现了某个接口。)checkcast与其很像,但是不改变操作数栈。

5.ldc指令从运行时常量池中加载常量值,并把它推入操作数栈。

6.7 Go语言语法

Go语言的包不能相互依赖。

Go语言保证初始化空间值为0;

猜你喜欢

转载自blog.csdn.net/monkeydcoding/article/details/81303587