一.加载指的是将类的class文件读入到内存,并为之创建一个java.lang.Class对象,也就是说,当程序中使用任何类时,系统都会为之建立一个java.lang.Class对象。具体过程如下:
1.通过类的全限定名称获取定义此类的二进制流;
2.将字节流所代表的静态存储结构转化为方法区运行时数据结构;
3.在方法区生成对应类的对象,作为方法区这个类的数据访问入口
类的加载由类加载器完成,类加载器通常由JVM提供,这些类加载器也是前面所有程序运行的基础。具体如下:
1.引导类加载器(BootstrapClassLoader):由C/C++实现,用于加载Java的核心类库,如Runtime包下的类;也负责加载其他加载器;只能加载包名为java,javax,sun开头的类
2.扩展类加载器(ExtensionClassLoader):由Java实现,派生于ClassLoader;用于加载Java.ext.dirs指定的目录和JDK的安装目录下的jre/lib/ext子目录的内容
3.系统类加载器(SystemClassLoader):由Java语言编写,派生于ClassLoader;加载环境变量classpath/path指定路径下的类库;作为程序的默认类加载器,一般Java应用类由它加载完成;
4.除此之外,开发者可以通过继承ClassLoader基类来创建自己的类加载器。
二.链接阶段:当类被加载之后,系统为之生成一个对应的Class对象,接着将会进入连接阶段,连接阶段负责把类的二进制数据合并到JRE中。类连接又可分为如下3个阶段。
1.验证:目的是为了确保class文件的字节流符合虚拟机的要求;包括文件格式验证,元数据验证,字节码验证,符号引用验证。
文件格式验证:主要验证字节流是否符合Class文件格式规范,并且能被当前的虚拟机加载处理。例如:主,次版本号是否在当前虚拟机处理的范围之内。常量池中是否有不被支持的常量类型。指向常量的中的索引值是否存在不存在的常量或不符合类型的常量。
元数据验证:对字节码描述的信息进行语义的分析,分析是否符合java的语言语法的规范。
字节码验证:最重要的验证环节,分析数据流和控制,确定语义是合法的,符合逻辑的。主要的针对元数据验证后对方法体的验证。保证类方法在运行时不会有危害出现。
符号引用验证:主要是针对符号引用转换为直接引用的时候,是会延伸到第三解析阶段,主要去确定访问类型等涉及到引用的情况,主要是要保证引用一定会被访问到,不会出现类等无法访问的问题。
2.准备:为类变量进行内存分配,并设置为默认初始值(即零值);
常量在编译时进行内存分配,同时进行初始化;
实例对象在实际创建对象时分配。
3.解析:将常量池内的符号转为已用;说明一下:符号引用:符号引用是以一组符号来描述所引用的目标,符号可以是任何的字面形式的字面量,只要不会出现冲突能够定位到就行。布局和内存无关。直接引用:是指向目标的指针,偏移量或者能够直接定位的句柄。该引用是和内存中的布局有关的,并且一定加载进来的。
三.初始化阶段:执行构造器方法<clinit>()的过程,此方法是javac收集变量赋值动作和静态代码块合并而来,为类的静态变量赋予正确的初始值。
如果类中有语句:private static int a = 10,它的执行过程是这样的,首先字节码文件被加载到内存后,先进行链接的验证这一步骤,验证通过后准备阶段,给a分配内存,因为变量a是static的,所以此时a等于int类型的默认初始值0,即a=0,然后到解析,到初始化这一步骤时,才把a的真正的值10赋给a,此时a=10。
类加载时机
创建类的实例,也就是new一个对象
访问某个类或接口的静态变量,或者对该静态变量赋值
调用类的静态方法
反射(Class.forName("com.lyj.load"))
初始化一个类的子类(会首先初始化子类的父类)
JVM启动时标明的启动类,即文件名和类名相同的那个类