深入java虚拟机——类加载器

    类加载器完全可以说是是 Java 语言的一个创新,也是 Java 语言流行的重要原因之一。它使得 Java 类可以被动态加载到 Java 虚拟机中并执行。

    Java 虚拟机使用 Java 类的方式如下:Java 源程序(.java 文件)在经过 Java 编译器编译之后就被转换成 Java 字节码(.class 文件)。类加载器负责读取 Java 字节码,并转换成java.lang.Class类的一个实例。每个这样的实例用来表示一个 Java 类。通过此实例的 newInstance()方法就可以创建出该类的一个对象。实际的情况可能更加复杂,比如 Java 字节代码可能是通过工具动态生成的,也可能是通过网络下载的。基本上所有的类加载器都是 java.lang.ClassLoader类的一个实例。

    首先,我们讲一下类的加载过程。

      JVM将类加载过程分为三个步骤:装载(Load),链接(Link)和初始化(Initialize)。其中链接又分为:验证,准备,解析三个阶段。各个步骤执行操作如下:

      1) 装载:查找并加载类的二进制数据;

      2)链接:

验证:确保被加载类的正确性;

准备:为类的静态变量分配内存,并将其初始化为默认值;

解析:把类中的符号引用转换为直接引用;

      3)初始化:为类的静态变量赋予正确的初始值;

      也需要说明一下,验证这一步存在的必要性。

      如果由编译器生成的class文件,它肯定是符合JVM字节码格式的,但是万一有高手自己写一个class文件,让JVM加载并运行,用于恶意用途,就不妙了,因此这个class文件要先过验证这一关,不符合的话不会让它继续执行的,也是为了安全考虑吧。

 

      接下来,我们看一下类的加载具体操作。

      类的加载指的是将类的.class文件中的二进制数据读入到内存中,将其放在运行时数据区的方法区内,然后在堆区创建一个这个类的java.lang.Class对象,用来封装类在方法区类的对象。由此我们可以得出:

    (1)类的加载的最终产品是位于堆区中的Class对象;

    (2)Class对象封装了类在方法区内的数据结构,并且向Java程序员提供了访问方法区内的数据结构的接口。

      JVM的类加载是通过ClassLoader及其子类来完成的,类的层次关系和加载顺序可以由下图来描述:

《深入java虚拟机——类加载器》
 Java 中的类加载器大致可以分成两类,一类是系统提供的,另外一类则是由 Java 应用开发人员编写的。系统提供的类加载器主要有下面三个:

(1)引导类加载器(bootstrap class loader):它用来加载 Java 的核心库,是用原生代码来实现的,并不继承自 java.lang.ClassLoader

(2)扩展类加载器(extensions class loader):它用来加载 Java 的扩展库。Java 虚拟机的实现会提供一个扩展库目录。该类加载器在此目录里面查找并加载 Java 类。

(3)系统类加载器(system class loader):它根据 Java 应用的类路径(CLASSPATH)来加载 Java 类。一般来说,Java 应用的类都是由它来完成加载的。可以通过 ClassLoader.getSystemClassLoader()来获取它。

      除了系统提供的类加载器以外,开发人员可以通过继承 java.lang.ClassLoader类的方式实现自己的类加载器,以满足一些特殊的需求。

      真正完成类的加载工作是通过调用 defineClass来实现的;而启动类的加载过程是通过调用 loadClass来实现的。前者称为一个类的定义加载器(defining loader),后者称为初始加载器(initiating loader)。在 Java 虚拟机判断两个类是否相同的时候,使用的是类的定义加载器。也就是说,哪个类加载器启动类的加载过程并不重要,重要的是最终定义这个类的加载器。两种类加载器的关联之处在于:一个类的定义加载器是它引用的其它类的初始加载器。

      如类 com.example.Outer引用了类com.example.Inner,则由类 com.example.Outer的定义加载器负责启动类 com.example.Inner的加载过程。方法 loadClass()抛出的是 java.lang.ClassNotFoundException异常;方法 defineClass()抛出的是java.lang.NoClassDefFoundError异常。

      类加载器在成功加载某个类之后,会把得到的 java.lang.Class类的实例缓存起来。下次再请求加载该类的时候,类加载器会直接使用缓存的类的实例,而不会尝试再次加载。也就是说,对于一个类加载器实例来说,相同全名的类只加载一次,即 loadClass方法不会被重复调用。

 

程序猿行业技术生活交流群:181287753(指尖天下),欢迎大伙加入交流学习。

    原文作者:java虚拟机
    原文地址: https://blog.csdn.net/iteye_663/article/details/82616309
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞