类的加载指的是将类的.class文件中的二进制数据读入到内存中,将其放在运行时数据区的方法区内,而后在堆区建立一个java.lang.Class对象,用来封装类在方法区内的数据结构。 java
类的加载并非首次主动使用时加载,而是JVM在“预料”到某个类将要使用时预先加载。若是在预先加载的过程当中遇到了.class文件缺失或存在错误,类加载器在程序首次主动使用该类时才报错(LinkageError错误),若是这个类一直没有被主动使用,那么类加载器就不会报错。数组
加载.class文件时,能够从本地直接加在;能够从zip包中读取;能够运行时动态代理生成;也能够从网络加载;及其余文件转换(如jsp)。安全
JVM类加载分五个阶段:加载,验证,准备,解析,初始化。网络
加载、验证、准备和初始化这四个阶段发生的顺序是固定的,而为了支持动态绑定,解析阶段在某些状况下能够在初始化阶段以后开始。同时这几个阶段是按顺序开始,而不是按顺序进行或完成,这些阶段一般都是互相交叉地混合进行的,在一个阶段执行的过程当中调用或激活另外一个阶段。数据结构
加载:这个阶段会在内存中生成一个表明这个类的java.lang.Class对象,做为方法区这个类的各类数据的入口。jvm
验证:验证Class文件的字节流中包含的信息是否符合当前虚拟机的要求,而且不会危害虚拟机自身的安全。验证阶段不是必须的,它对程序运行期没有影响,若是所引用的类通过反复验证,那么能够考虑采用-Xverify:none参数来关闭大部分的类验证措施,以缩短虚拟机类加载的时间。jsp
准备:正式为类变量分配内存并设置类变量的初始值阶段,即在方法区中分配这些变量所使用的内存空间。布局
此时进行内存分配的仅包括类变量(static),而不包括实例变量,实例变量会在对象实例化时随着对象一块分配在Java堆中spa
这里的初始值是数据类型默认的零值(如0、null、false等)而不是代码里面的初始化参数时所赋的值。代理
解析:解析阶段主要针对类或接口、字段、类方法、接口方法、方法类型、方法句柄和调用点限定符7类符号引用,虚拟机将常量池中的符号引用替换为直接引用。
符号引用以一组符号来描述所引用的目标,这些信息必须足以惟一的识别一个类、字段、方法;直接引用能够是指向目标的指针,相对偏移量或是一个能间接定位到目标的句柄。
不一样jvm的内存布局可能不一样,直接引用与jvm的内存布局相关,而符号引用与内存布局不相关。
初始化:初始化阶段是执行类构造器<clint>
方法的过程。<clint>
方法是由编译器自动收集类中的类变量的赋值操做和静态语句块中的语句合并而成的。虚拟机会保证<clint>
方法执行以前,父类的<clint>
方法已经执行完毕。
类加载器
JVM提供了三种类加载器:
Bootstrap ClassLoader
):负责加载 JAVA_HOME\lib 目录中的,或经过-Xbootclasspath
参数指定路径中的,且被虚拟机承认(按文件名识别,如rt.jar)的类,由C++实现,不是ClassLoader的子类。Extension ClassLoader
):负责加载 JAVA_HOME\lib\ext 目录中的,或经过java.ext.dirs系统变量指定路径中的类库,java实现,ClassLoader的子类。Application ClassLoader
):负责加载用户路径(classpath)上的类库,java实现,ClassLoader的子类。以及自定义类加载器,应用程序根据自身须要自定义的类加载器。
JVM经过双亲委派模型进行类的加载:若是一个类加载器收到了类加载的请求,它首先不会本身去尝试加载这个类,而是把请求委托给父加载器去完成,依次向上,所以,全部的类加载请求最终都应该被传递到顶层的启动类加载器中,只有当父加载器在它的搜索范围中没有找到所需的类时,即没法完成该加载,子加载器才会尝试本身去加载该类。
采用双亲委派模型,无论是哪一个加载器加载这个类,最终都是委托给顶层类加载器进行加载,防止内存中出现多份一样的字节码。
双亲委派机制仅仅是java 规范所推荐的一种实现方式,并非强制性的要求,如OSGI就采用网络图庄的类加载模型,而非双亲委派机制。相比而言网状的类加载模型比双亲委派的层次化模型在加载类时效率更高,无需在classpath一长串列表中进行查找;同时错误提示更加友好。