在Java class文件里占很大一部分是常量池:类名,变量名,变量初始值等。
在class文件的字段表和方法表里都需要引用常量池内的数据,这个索引称为符号引用。
一个类在虚拟机的加载过程分为:加载,验证,准备,解析,初始化
加载是将class装入内存,将其静态结构转化为方法区运行时结构,并产生一个Java.lang.Class对象,这个对象会使用类加载器继续创建该类
验证是对class文件格式,数据,字节码,符号引用进行验证
准备是将static赋0,常量赋初始值。具体赋值行为在初始化之后。
解析是将符号引用转换为直接引用。直接引用是直接指向某内存地址,一般解析在编译阶段即可确定的方法。
初始化是使用类加载器同步的加载类文件。对于任意一个类,都需要由加载它的类加载器和这个类本身一同确立其在Java虚拟机中的唯一性。对同一个class类文件,手动加载的类和自动加载的类并不相同。
加载器分为启动类加载器,扩展类加载器,应用程序类加载器。
如果一个类加载器收到了类加载的请求,它首先不会自己去尝试加载这个类,而是把这个请求委派给父类加载器去完成,这个过程叫做双亲委派模型。
有且只有5种情况必须立即对类进行“初始化”:
new、getstatic、putstatic或invokestatic这4条字节码指令时
使用java.lang.reflect包的方法对类进行反射调用的时候
初始化一个类的时候,如果发现其父类还没有进行过初始化
当虚拟机启动时,用户需要指定一个要执行的主类(main)
使用JDK 1.7的动态语言相关
其他均属于被动引用,不会造成初始化,特别注意子类调用父类的static字段,或者某类的final字段都不会触发初始化。
Java虚拟机运行是一个线程一个栈,每个栈中含有多个嵌套的栈帧。
每个栈帧含有局部变量表、操作数栈、动态连接、方法返回地址等,是一个方法的执行空间
局部变量表最小访问单元是slot,每个slot可以存放一个32bit数据,对于64位使用相邻两个slot存取
操作数栈类似堆栈机运行时的数据
动态连接是栈帧所属的方法的引用
一切方法调用在Class文件里面存储的都只是符号引用,而不是方法在实际运行时内存布局中的入口地址,所以需要解析调用或者分派调用。
分派又分为静态多分派、和动态单分派。静态对应重载,动态对应重写。
静态多分派是指在编译阶段可以确定参数的类,在常量池中可以找到对应的符号引用。
动态单分派是在静态编译期的基础上确定方法之后,针对方法接收者进行调用。一般和C++一样采用虚方法表实现。