JVM学习-类的装载

我们在eclipse中写好的类,编辑器会自动帮我们编译好生成Class文件,但是JVM如何使用这种字节码文件呢?这就需要类的加载,类的加载是Java语言的一个创新点,它不像c/c++这些静态编译语言,c++会在编译阶段完成时生成可以执行的机器码,但是Java类的加载与运行都是动态进行的。即:Java类的加载是在程序运行期间完成。

Load的时机

Java类的加载可以描述成一种惰性加载,就是程序运行时需要加载了才去加载它。所以什么时候去加载类就是一个必须得讨论的问题。一般触发Java类的加载有四种情况:

  1. 遇到new等关键字时如果发现类还没有加载就会去加载这个类。常见的加载场景有:使用new关键字实例化对象的时候,读取或者设置一个类的静态字段(被final修饰、已在编译期把结果放入常量池的除外),以及调用一个类的静态方法的时候。

  2. 使用java.lang.reflect包的方法对类进行反射调用的时候,如果类没有进行过初始化,则需要先触发其加载。

  3. 初始化一个类的时候发现其父类还没有进行初始化,则需要先触发其父类的初始化。

  4. 当虚拟机启动的时候,会把包含main方法的类加载。

上述称为把一个类进行主动引用,除这些之外不能引起类的初始化的被称为被动引用,如以下几个场景:

  1. 通过子类引用父类的静态字段,不会导致子类初始化。

  2. 通过数组定义来引用类不会触发类的初始化。

  3. 常量(static&final)在编译阶段就会被存入调用类的常量池,本质上没有直接引用到定义常量的类,因此不会触发类的初始化。

  4. 接口在初始化的时候并不要求所有父类接口进行初始化,只有在真正使用到父接口的时候才会去加载这个父接口。

Load过程

JVM中类加载的全过程会包括加载、验证、准备、解析和初始化这5个阶段。这部分不过细展开了,所做的工作通过字面就可以推理出来。

类加载器

在java中,类的唯一性并不是由类本身决定的,需要加上一条判断条件就是加载这个类的类加载器,也就是说在JVM中两个类相同必须具备两个条件就是:Class文件相同和使用相同的类加载器。

Java中的类加载器主要有以下三种,由上到下是:

       启动类加载器:用来加载JAVA_HOME/lib下的java类库,即Java的核心类库

      扩展类加载器:用来加载Java的扩展库

     系统类加载器:用来通过classpath来加载java类,一般java应用的类就是这个加载器来完成的。可以通过 ClassLoader.getSystemClassLoader()来获取它。

Java推荐使用双亲委派模型来组织类加载器之间的关系,何为双亲委派模型?就是:如果一个类加载器收到了类加载的请求,它首先不会自己去尝试加载这个类,而是把请求委派给父类加载器去完成,每一个层次的类加载器都是如此,只有当父类加载器反馈自己无法加载时,子类加载器才会去尝试自己加载。因此所有的加载请求都会传到顶层的加载器(即启动类加载器)中。



Previous     Next
zhing /
Published under (CC) BY-NC-SA in categories java  tagged with java