简介
虚拟机把描述类的数据从class文件加载到内存,并对数据进行校验、转换解析、和初始化,最终形成可以被虚拟机直接使用的java类型,这个过程就是虚拟机的类加载
java语言中,类型的加载、连接和初始化过程都是在程序运行期间完成的,这种策略使得类加载时增加了一些性能开销,但是却为java提供了更高的灵活性;
类的声明周期: 加载 ------验证------准备------解析------初始化------使用------卸载
上述声明周期是固定,但是有个例外,就是解析可能在初始化之后,比如运行时绑定
初始化时机
已经初始化过,则不再进行;下面jvm规定的初始化条件
1) 遇到new、gatstatic、putstatic或者invokestatic这4条指令
2)使用java.lang.reflect包方法进行反射调用时
3)初始化类时,需要对父类进行初始化
4)当虚拟机启动时,主类(包含main方法的类)进行初始化
5)java.lang.invoke.MethodHandle实例后解析结果REF_getStatic,REF_putStatic,REF_invokeStatic的方法句柄,这些句柄对应的类需要初始化
上面5个,我就知道,java使用new,反射,启动主类,其它的,求告诉;
注意: 除了这5中,其它形式的类引用,不会初始化;对于静态字段,方法,只初始化定义的类;常量,常量在编译阶段存储到常量池中,不需要对所在类进行初始化
加载
1)通过一个类的全限定名来获取定义的二进制流
2)二进制流所代表的静态存储结构转化为方法区的运行时数据结构
3)内存中生成一个代表这个类的java.lang.Class对象,作为方法区这个类的各种数据访问入口
获取二进制流,可以从zip包、网络、运行时计算生成、其它文件(jsp)、数据库等等
获取二进制流的过程: 1)数组由jvm虚拟机控制,2)其它默认由虚拟机控制,程序员可以自定义类加载器来处理
验证
确保Class文件的字节流中包含的信息是符合当前虚拟机要求的,并且不会危害虚拟机自身的安全
1)文件格式校验
2)元数据校验
3)字节码校验
4)符号引用
准备
正式为类变量分配内存空间并设置类变量初始值的阶段,内存在方法区中分配;分配内存的类变量,不包括实例变量,通常情况下是数据类型的0值,但是常量,则按照赋值来初始化
解析
虚拟机将常量池内的符号引用替换为直接引用的过程
1)类或接口的解析
2)字段解析
3)类方法的解析
4)接口方法的解析
初始化
根据程序员通过程序制定的主观计划去初始化类变量和其它资源;也即执行类构造器<clinit>() 方法的过程
<clinit>() 方法
1)由编译期自动搜集类中所有变量的赋值动作和静态语句块中的语句合并产生的;静态语句块只能访问定义语句块之前的变量,定义在后面的静态语句块,只能赋值,不能访问
2)与类构造函数不同,不需要显示调用父类构造器,虚拟机保证子类方法执行之前,父类的<clinit>() 方法已经执行
3)父类优先子类先执行
4)对于类和接口并不是必需的
5)接口中不能使用静态语句块,但可以有变量初始化操作
6)在多线程中进行,保证正确的加锁,同步;
重点来了。。。
类加载器
加载步骤1这个动作放在了java虚拟机外部去实现,以便应用程序自己去决定如何去获取所需要的。实现这个动作的模块成为类加载器
每个类加载器都拥有一个独立的类名称空间。所以类和加载这个类的加载器决定了这个类是否唯一
双亲委派模型
从虚拟机的角度来看,只存在两种不同的类加载器:
1)启动类加载器(Bootstrap ClassLoader),C++实现,虚拟机的一部分
2)其它加载器,由java语言实现,独立于虚拟机之外,并且继承java.lang.ClassLoader
从java开发人员来看,有下面几种
1)启动类加载器:就是上面的1),负责加载<JAVA_HOME>/lib目录中或者-Xbootclasspath参数所指定路径中并且可被虚拟识别的类库;程序员无法使用此加载器
2)扩展类加载器:负责加载<JAVA_HOME>/lib/ext目录或者被java.ext.dirs系统变量所制定路径中所有库类;开发者可以直接使用扩展加载器
3)应用程序类加载器:这个类加载器,是ClassLoader中的getSystemClassLoader()方法的返回值;负责加载用户类路径上所指定的类库,开发者可以直接使用这个类加载器;程序中没有定义过自己的加载器,则这个就是默认的类加载器
加载器继承关系:------------表示继承
应用程序类加载器------------扩展类加载器------------启动类加载器
java设计者希望 自定义类加载器------------应用程序类加载器
双亲委托模型:如果一个类加载器收到了类加载请求,它首先不会自己去尝试加载这个类,而是把这个请求委派给父类加载器去完成,每个类加载器都是如此,所以所有的加载请求最终都应该传送到顶层启动类加载器,只有当父类加载器自己无法完成加载时,子加载器才会尝试去加载
双亲委派模型,类加载器具有优先级的层次关系,保证了java程序的稳定运行
网友评论