【JVM】虚拟机类加载机制
什么是类加载
1、类加载的时机
类的生命周期
其中,加载、验证、准备、初始化和卸载这5个顺序是固定的,即类的加载过程按这个顺序开始(但并不是按照这个顺序进行或完成,而是各个阶段交叉进行)。
这5种场景中的行为称为对一个类的主动引用,除此之外都不会触发类的初始化,被称为被动引用。被动引用的情况有以下几种:①通过子类引用父类的静态字段,不会导致子类初始化,只会导致父类的初始化;②通过数组定义来引用类,不会触发类的初始化(但会触发数组初始化);③常量在编译阶段会存入调用类的常量池中,本质上并没有直接引用到定义常量的类,因此不会触发定义常量的类的初始化。 接口初始化的类的初始化的区别:类在初始化时,要求其父类已经初始化,但一个接口在初始化时,并不要求其父接口全部都完成初始化,只有在真正使用的父接口的时候才会初始化。 类加载的过程1、加载在加载阶段,虚拟机需要完成3件事情:
2、验证验证的目的是为了确保Class文件的字节流中包含的信息符合当前虚拟机的要求,并且不会危害虚拟机自身的安全。验证阶段大致会完成下面4个阶段的检验动作:
验证阶段是非常重要但不是一定必要的阶段,如果所运行的代码(自己编写的以及第三方包中的代码)已经被反复使用和验证过,可以考虑使用 3、准备准备阶段的工作是:为类变量分配内存并设置类变量初始值。这些变量所使用的内存都将在方法区进行分配。要注意的是:①类变量是指被static修饰的变量,不包括实例变量,实例变量将会在对象实例化时随着对象一起分配在Java堆中;②初始值在通常情况下是指数据类型的零值,但如果是 private int value = 123;//不会为该变量分配内存 private static int value = 123;//会为该变量分配内存并赋为零值(0) private static final int value = 123;//会为该变量分配内存并赋值为123
4、解析解析阶段的工作:虚拟机将常量池内的符号引用替换为直接引用的过程。解析动作主要针对类或接口、字段、类方法、接口方法、方法类型、方法句柄和调用点限定符。
5、初始化初始化:真正开始执行类中定义的Java代码(或者说字节码)。 关于 ① public class Test{ static { i = 0;//赋值可以通过编译 System.out.print(i);//这句访问则会导致编译器报错:非法向前引用 } static int i = 1; }
② ③由于父类的 ④ ⑤接口中不能使用静态语句块,但仍然有变量初始化的赋值操作,因此接口与类一样都会生成 ⑥虚拟机会保证一个类的 类加载器类加载器的作用:通过一个类的全限定名来获取描述此类的二进制字节流。这个动作是在Java虚拟机外部实现的,以便让应用程序自己决定如何去获取所需要的类。类加载器在类层次划分、OSGi、热部署、代码加密等领域大放异彩。 类与类加载器对于任意一个类,都需要由加载它的类加载器和这个类本身一同确立其在Java虚拟机中的唯一性:比较两个类是否“相等”,只有在这两个类是由同一个类加载器的前提下才有意义,否则即使这两个类来源于同一个Class文件,被同一个虚拟机加载,只要加载它们的类加载器不同,那这两个类就必定不相等。 这里所指的相等,包括代表类的Class类的对象的 双亲委派模型从Java虚拟机的角度来讲,只存在2种不同的类加载器: 从Java开发人员的角度看,类加载器可以划分的更详细,分为4种:
应用程序均是由这4种类加载器互相配合进行加载的,他们之间的关系如下图:
类加载器双亲委派模型
类加载器这种层次关系称为类加载器的双亲委派模型:除了顶层的启动类加载器外,其余的类加载器都应当有自己的父类加载器。这里的类加载器之间的父子关系一般不会以继承的关系实现,而是都使用组合关系来复用父加载器的代码。 双亲委派模型的工作过程:如果一个类收到了类加载的请求,它首先不会自己去尝试加载这个类,而是把这个请求交给父类加载器去完成,每一层次的类加载器都是如此。因此所有的加载请求都是从顶层的启动类加载器开始,只有当父加载器反馈自己无法完成这个加载请求,子类才会尝试自己去加载。 使用双亲委派模型的好处:Java类随着它的类加载器一起具备了一种带有优先级的层次关系,比如保证不会出现两个 双亲委派模型的实现代码( protected Class<?> loadClass(String name,boolean resolve)throws ClassNotFoundException{ synchronized (getClassLoadingLock(name)) { // 首先检查该类有没有被加载过 Class<?> c = findLoadedClass(name);// if (c == null) {//说明之前该类没加载过 long t0 = System.nanoTime(); try { if (parent != null) { //让父类加载器取加载 c = parent.loadClass(name,false); } else { //让启动类加载器去加载 c = findBootstrapClassOrNull(name); } } catch (ClassNotFoundException e) { //发生ClassNotFoundException异常,则说明父类加载器没有加载成功 } if (c == null) {//为空,说明父类加载器没有加载成功 long t1 = System.nanoTime(); //调用自身findClass(String name)方法去进行类加载 c = findClass(name); // this is the defining class loader; record the stats sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0); sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1); sun.misc.PerfCounter.getFindClasses().increment(); } } if (resolve) { resolveClass(c); } return c; } }
破坏双亲委派模型(//todo)
内容来自《深入理解Java虚拟机》:虚拟机类加载机制 (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |