JVM系列:四、Java类加载机制总结

来源:互联网 发布:iface102下载数据 编辑:程序博客网 时间:2024/06/04 01:14

Java程序是运行与虚拟机之上的,程序中使用到的类元数据由虚拟机先加载进内存才可以使用。
虚拟机的类加载机制:虚拟机把描述类的数据从Class文件加载到内存,并对数据进行校验、转换解析和初始化,最终形成可以被虚拟机直接使用的Java类型。

一、类加载的步骤

Java的类型加载和连接过程都是在程序运行期间完成的。

类的生命周期共分为5步
1.加载 -> 2.连接【2.1.验证 -> 2.2.准备 -> 2.3.解析】 -> 3.初始化 -> 4.使用 -> 5.卸载

Java规范规定只有四种情况必须立即对类初始化

  1. 遇到new、getstatic、putstatic、invokestatic4条指令时,若没被初始化,则先触发初始化(这4条指令即实例化对象,读取或设置类的静态字段,调用类的静态方法)
  2. 使用java.lang.reflect包的方法进行反射调用时,若没被初始化,则先触发初始化
  3. 初始化一个类时,其父类还未初始化时,父类进行初始化
  4. 虚拟机启动时,先初始化主类(main方法的那个类)

二、类加载的过程

类加载包括加载、验证、准备、解析和初始化。

  1. 加载:主要作用是获取定义类的二进制字节流,转化为方法区的运行时数据结构,最后在堆中生成这个类的java.lang.Class对象,作为方法区中该数据结构的访问入口。
  2. 验证:主要是为了确保Class文件的字节流信息符合当前虚拟机的要求。如果不符合Class文件存储格式,会抛出一个java.lang.VerifyError异常或其子类异常。过程大致包括四个检验:文件格式验证、元数据验证、字节码验证和符号引用验证。
  3. 准备:正式为类变量分配内存并设置类变量初始值,这些内存都将在方法区中分配。
  4. 解析:该阶段是虚拟机将常量池中的符号引用替换为直接引用的过程。解析动作主要针对类或接口、字段、类方法、接口方法。
  5. 初始化:初始化阶段是执行类构造器<clinit>()。<clinit>()是由类中所有类变量的赋值动作和静态语句块(static{}块)合并产生的,且父类的<clinit>()先执行。该方法是同步安全的。

三、类加载器

虚拟机外部的应用程序可以用类加载器来通过类的全限定名获取此类的二进制字节流。比较两个类是否“相等”,前提是由同一个类加载器加载的。

对于Java虚拟机来说,类加载器分为两种:启动类加载器和其他类加载器,其中其他类加载器继承自java.lang.ClassLoader的类加载器。

对于开发者来说,类加载器分为三种:

  1. 启动类加载器:C++语言实现,虚拟机自身的一部分。放在<JAVA_HOME>\lib目录中。
  2. 扩展类加载器:由sun.misc.Launcher$ExtClassLoader实现,负责加载<JAVA_HOME>\lib\ext目录中或java.ext.dirs系统变量指定的目录的所有类库。
  3. 应用程序类加载器:由sun.misc.Launcher$AppClassLoader实现,也称为系统类加载器。

还可以自定义自己的类加载器。类加载器有层级关系的:

这些层级之间并不一定是继承来实现的,而是以组合实现的。

加载过程:一个类加载器收到类加载的请求,先把请求委派给父类加载器去完成,父类加载器反馈自己无法完成这个加载请求(搜索范围中没有找到所需的类),子加载器才会尝试自己去加载。
如rt.jar中的类,都是在启动类加载器中加载的,无论哪个类加载器要加载这个类,最终都委派给启动类加载器,因此类在程序的各个类加载器环境中都是同一个类。
这种机制称为双亲委派模型,当也有不符合这种模型的情况,如OSGi。

OSGi是当前最热门的Java模块化标准,它能实现模块化热部署,实现热部署的关键是每一个程序模块(bundle)都有一个自己的类加载器,当需要更换一个bundle时就把bundle连同类加载器一个换掉以实现热替换。

1 0