JVM由一个类装载子系统,执行引擎,运行时数据区和本地方法接口组成。
类装载子系统
主要职责:根据全限定名来装载类型(同一限定名通过命名空间区分)。
主要分类:启动类装载器,系统类装载器和用户自定义装载器
启动类装载器只在系统类的安装目录中查找,系统内装载器在CLASSPATH包含的子目录中查找,这两种类装载器主要负责类型的装载,用户自定义类装载器就是java程序的一部分,通过defineClass,findSystemClass,resolveClass与JVM相连
执行引擎
主要职责:负责执行包含在装载类的方法中的指令。
运行时数据区
主要职责:JVM运行一个程序时,用来储存许多东西。
主要分类:方法区,堆,程序计数器,java栈,本地方法栈
方法区
- 最核心的功能?
用来储存装载类型的信息。 - 如何获取装载类型的信息?
当虚拟机装载某个类型时,使用类装载器搜索到某个class文件,也就是二进制数据流,传输到虚拟机中,提取类型信息到方法区。 - 如何储存?
选择合理的数据结构,由设计者决定 - 类型信息的内容?
基本信息:类/超类全限定名或者超接口全限定名的有序列表,类类型或接口类型,访问修饰符
扩展信息:常量池,字段信息,方法信息,类(静态)变量,指向ClassLoader类的引用,指向Class类的引用- 常量池
直接常量+对其它类型,字段或方法的引用。通过索引访问 - 字段信息
字段名,字段类型,字段修饰符 - 方法信息
方法名,返回类型,参数,修饰符 - 类(静态)变量
编译时常量:final声明或初始化的类变量。一般的类变量作为声明它们类型的一部分数据而保存,编译时常量作为使用它们类型的一部分而保存 - 指向ClassLoader类的引用
类型装载时,确定是哪种类装载器装载的 - 指向Class类的引用
关联Class对象实例和数据类型信息。通过forName方法或getClass方法获取该引用,没有则抛出ClassNotFoundException异常。
- 常量池
方法区示例
class Test{
public static void main(String args[]){
Demo demo = new Demo();
demo.aa();
}
}
class Demo{
void aa();
}
运行该test程序,虚拟机找到并读入相应的class文件”Test.class”,从该二进制数据流中提取类型信息并放到方法区。通过保存在方法区的字节码,执行main方法,虽然Demo还没有被装载,但此时Test的常量池中有一个”Demo”的符号引用,通过搜索(散列表,搜索树等),forName,getClass等方式找到Demo类的引用,若虚拟机还没有装载Demo类,通过类装载子系统来进行类型信息的获取,并保存到方法区,将该引用替换Test常量池中的第一项,之后就可以快速访问Demo类了,替换的过程叫做常量池的解析。虚拟机并不是把程序中用到的所有类都装载才开始运行程序,而是只有在需要时才装载相应的类,这也是动态的一种体现吧。虚拟机为Demo对象分配内存(虚拟机能够根据方法区的类型信息来确实对象的内存大小),并把Demo对象压栈,完成了main指令。