类的加载机制
Jvm的运行流程
编译为class文件为统一完成,但是class的加载只有在调用时才启用。
跨平台的原因是不同平台的jdk封装了不同平台的指令,将代码解析为对应平台识别的机器指令。
类的加载流程图
加载,连接(验证,准备,解析),初始化,使用,卸载
加载:查找和导入Class文件
根据全限定类名,将class文件以字节流方式读入方法区,并生成Class对象。
通过一个类的全限定类名,来获取定义该类的二进制字节流;
随后,将该字节流所代表的静态存储结构,转化为方法区的运行时数据结构;
在方法区生成一个java.lang.Class对象,作为方法区内该类的各种数据的访问入口
连接
验证:在于确保Class文件的字节流中包含信息符合当前虚拟机要求。
主要包括四种验证,文件格式验证,元数据验证,字节码验证,符号引用验证。
文件格式验证:检验该字节流是否满足class文件格式(例如:开头是魔数,主次版本号是否是当前虚拟机处理范围之内,即是否在次到主之间的版本,高于主的版本不加载(这就是低版本的jdk无法执行高版本jdk的原因),等等),检验过后,执行"加载"部分的第二件事,将数据放入方法区并存储
元数据验证:保证类的元数据信息符合java语言规范(例如:这个类若是普通类,是否实现了其实现接口下的所有类)
字节码验证,确定指令顺序符合逻辑
符号引用验证: 将符号引用转化为直接引用的过程
准备:分配空间以及设置初始值
类中的static变量分配内存(方法区),并设置默认值为0(eg.static int x = 123;//这时候x = 0)。
类中的final变量分配内存(常量池),并设置为期望值(eg.final int y = 456;//这时候y = 456)。
类对象实例创建时,在堆中给实例变量分配内存空间。
解析:将常量池内的符号引用替换为直接引用的过程
Java文件在编译为Class文件时,如对于其他类的引用,此时使用符号引用的方式进行代替,存储在class文件的常量池中;在类加载过程中,解析时,将符号引用转化为指向实际地址的直接引用,并存储在运行时常量池中。
初始化:对静态变量,静态代码块执行初始化工作
执行静态块(static{})、初始化static变量(eg.static int x = 123;//"准备"阶段后,x = 0;"初始化"后,x = 123)、执行构造器
发生的时机:外部调用new时启用(或者反射调用类的情况下)
使用:使用该类所提供的功能.
卸载:从内存中释放.
代码执行顺序
Static{}以及静态变量按照从上到下顺序执行,静态变量最开始为0后来为1(1,2);类的构造完成(3);执行入口main方法(4),执行main中涉及实例化时,调用构造方法。
生成class文件的途径:动态加载
JAR,WAR,EAR包获取
如Applet从网络中获取
如java动态代理技术,在运行时生成
如从jsp文件等其他文件中生成class文件
类什么时候被初始化
创建类的实例,也就是 new 一个对象
反射(Class.forName("com.lyj.load"))
访问某个类或接口的静态变量,或者对该静态变量赋值,或者调用类的静态方法
初始化一个类的子类(会首先初始化子类的父类)
JVM 启动时标明的启动类