JVM虚拟机结构如下图:
JVM虚拟机结构
JVM虚拟机主要由类加载器、JVM内存结构、执行引擎以及与本地方法接口等组成。其中JVM内存结构又由方法区、堆、栈、程序计数器、本地方法栈等组成。
类加载器(Classloader)类加载器负责将编译好得.class字节码文件加载装入到JVM内存中,使JVM可以实例化或以其它方式使用加载后得类。
加载类得过程:加载->验证->准备->解析->初始化
1.加载
加载时jvm做了这三件事:
1)通过一个类得全限定名来获取该class文件得二进制字节流。
2)将这个二进制字节流得静态存储结构转化为JVM方法区运行时要求得数据结构。
3)在JVM内存方法区中创建一个java.lang.Class得对象,作为该类数据得访问入口。
注意:java.lang.Class对象与我们通常讲得对象不是一回事。我们称为类对象,和实例对象。类对象是在JVM方法区,而且相同得Class对象在方法区中只能有一个;实例对象是在JVM堆中,可以创建多个。
获取Class对象得方式有三种:实例对象得getClass()方法、.class得方式、Class类得静态方法forName()。
Student stu = new Student();
stu.getClass();
Student.class;
Class.forName("com.Student");
2.验证
校验.class文件得正确性,确保class文件是符合规范定义得。
验证主要有:1)文件格式验证:验证字节流是否符合Class文件得规范,是否能被当前版本得虚拟机处理;2)元数据验证:进行语义分析,确保符合java语言规范,包括是否有父类,是否继承了不该被继承得类,类中得字段和方法是否与父类有矛盾等;3)字节码验证:通过数据流和控制流分析,确定语义是合法得,符合逻辑得;4)符号引用验证:这个校验在解析阶段发生;5)其它验证。
3.准备
为类分配内存,同时初始化类中得静态变量赋值为默认值。对于final static修饰得变量,直接赋值为定义值。
final static int a = 7;//准备阶段赋值为 7
public static b = 7;//准备阶段赋值为 0 (默认值)
4.解析
主要是把类得常量池中得符号引用解析为直接引用(如物理内存地址指针)。
5.初始化
初始化类中得静态变量,并执行类中得static代码、构造函数。当初始化一个类得时候,如果发现其父类还没有进行过初始化、则会先触发其父类得初始化。
初始化触发有以下几种:
a.通过new关键字、反射、clone、反序列化机制实例化对象时。
b.调用了类得静态方法。
c.通过反射调用类得方法时。
d.初始化该类得子类时。
e.JVM启动时被标记为启动类得类。
JVM内存结构JVM内存结构又由方法区、堆、栈、程序计数器、本地方法栈等组成。其中方法区、堆是所有线程共享得,栈、程序计数器、本地方法栈是线程私有得。
1.方法区:方法区是系统分配得一个内存逻辑区域,存放类加载器加载得类对象。
2.堆:堆用于存储对象实例以及数组值。堆中有指向类数据得指针,该指针指向了方法区中对应得类型信息。堆中还可能存放了指向方法表得指针。堆是所有线程共享得,所以在进行实例化对象等操作时,需要解决同步问题。此外,堆中得实例数据中还包含了对象锁,并且针对不同得垃圾收集策略,可能存放了引用计数或清扫标记等数据。
3.栈:栈是由多个栈帧组成得,一个栈帧对应一个方法调用。调用方法时会将栈帧压入到当前线程得栈中,方法返回时弹出栈帧并抛弃。栈得主要任务是存储方法参数、局部变量、中间运算结果,栈是线程私有得,保证了线程安全性。
4.程序计数器:线程私有(每个线程都有一个程序计数器),用于记住当前线程下一条JVM指令得执行地址。
5.本地方法栈:本地方法栈类似于Java栈,主要存储了本地方法调用得状态。不过虚拟机规范并没有强制要求本地方法栈,必须执行本地方法,各种虚拟机可以自由实现。在Sun JDK中,本地方法栈和Java栈是同一个。
执行引擎执行引擎是JVM执行Java字节码得核心,在不同得虚拟机实现里面,执行引擎在执行Java代码得时候可能会有解释执行(通过解释器执行)和编译器执行(通过即时编译器产生本地代码执行)两种选择,所有得Java虚拟机得执行引擎都是一致得:输入得是字节码文件,处理过程是字节码解析得等效过程,输出得是执行结果。


