Java类的加载过程
- 类从被加载到JVM中开始,到卸载为止,整个生命周期包括:
加载
、验证
、准备
、解析
、初始化
、使用
和卸载
七个阶段。 - 其中类加载过程包括
加载
、验证
、准备
、解析
和初始化
五个阶段。
加载(将Class文件转换成Class对象实例)
- 1.类加载器根据一个类的全限定名来读取此类的二进制字节流到JVM中,然后转换为一个与类目标对应的java.lang.Class对象实例。
- ①BootstrapClassLoader:负责加载$JAVA_HOME中jre/lib/rt.jar里所有的class,由C++实现,不是ClassLoader子类。
- ②ExtClassLoader:负责加载java平台中扩展功能的一些jar包,包括$JAVA_HOME中jre/lib/*.jar或-Djava.ext.dirs指定目录下的jar包。
- ③AppClassLoader:负责加载ClassPath中指定的jar包及目录中的class
- ④CustomClassLoader:属于应用程序根据自身需要自定义的ClassLoader,如tomcat,Jboss都会根据j2ee规范自行实现ClassLoader。
- defineClass方法将字节码的byte数组转换为一个类的class对象实例,如果希望在类被加载到JVM时就被链接,可以在自定义类加载器中调用resolve'Class方法。
-
自定义类加载器需要继承抽象类ClassLoader,实现findClass方法,该方法会在lodClass调用的时候被调用,findClass默认会抛出异常。
- findClass:表示根据类名查找类对象。
- loadClass:表示根据类名进行双亲委派模型进行类加载并返回类对象。
- defineClass:表示根据类的字节码转换为类对象。
-
自定义类加载器需要继承抽象类ClassLoader,实现findClass方法,该方法会在lodClass调用的时候被调用,findClass默认会抛出异常。
验证(验证数信息是否符合JVM规范,是否是一个有效的字节码文件)
- 验证内容:
类数据信息的格式验证,语义分析,操作验证
等。- 格式验证:
验证是否符合class文件规范
- 语义验证:
检查一个被标记为final的类型是否包含子类;检查一个类中的final方法视频被子类进行重写;确保父类和子类之间没有不兼容的一些方法声明(比如方法签名相同,但方法的返回值不同)
- 操作验证:
在操作数栈中的数据必须进行正确的操作,对常量池中的各种符号引用执行验证(通常在解析阶段执行,检查是否通过富豪引用中描述的全局定名定位到指定类型上,以及类成员信息的访问修饰符)
- 格式验证:
准备(为类追踪的所有静态变量分配内存空间,并为其设置一个初始值(由于还没产生对象,实例变量不在此操作范围内))
- 被final修饰的静态变量,会直接赋予原值。
- 类字段的字段属性表中存在ConstantValue属性,则在准备阶段,其值就是ConstantValue的值。
解析(将常量池中的符号引用转为直接引用(得到类或者字段,方法在内存中的指针或者偏移量,一遍直接调用该方法,这个可以在初始化之后再执行))
- 可以认为是一些静态绑定的会被解析,动态绑定则只会在运行时进行解析。
- 静态绑定包括一些final方法(不可重写),static方法(只属于当前类),构造器(不会被重写)
初始化(将一个类中所有被static关键字表示得代码统一执行一遍)
- 如果执行得是静态变量,那么就会使用用户指定得值覆盖之前在准备阶段设置得初始值
- 如果执行得是static代码块,那么在初始化阶段,JVM就会执行static代码块中定义的所有操作。
- 所有类变量初始化语句和静态代码块都会在编译时被前端编译器放在收集器里头,存放到一个特殊的方法中,这个方法就是<clinit>方法,即类/接口初始化方法。该方法的作用就是初始化一个中的变量,使用用户指定的值覆盖之前在准备阶段里设定的初始值,任何invoke之类的字节码都无法调用<clinit>方法,由JVM负责保证一个类的<clinit>方法执行之前,它的父类<clinit>方法已经被执行。
- JVM必须确保一个类在初始化的过程中,如果多线程需要同时初始化它,仅仅只能允许其中一个线程对其执行操作,其余线程必须等待,只有在活动线程执行完对类的初始化操作之后,菜会通知正在等待的其他线程。
网友评论