虚拟机类加载机制--类加载器

准备阶段的“通过一个类的全限定名来获取描述此类的二进制字节流”这个动作放到了Java虚拟机外部去实现,以便让应用程序自己决定如何如获取所需要的类。实现这个动作的代码模块称为“类加载器”

1.类与类加载器

每一个类加载器都有一个独立的类名称空间,由类加载器和类一起合作才能确定一个类在虚拟机中的唯一性。也就是说:比较两个类是否“相等”,即使他们来自同一个Class文件,在同一个虚拟机上被加载,如果加载它们的类加载器不同,那么这两个类就不相等。

这里的“相等”,包括类的Class对象的equals()方法、isAssignableFrom()方法、isInstance()方法,以及使用instanceof关键字作对象所属关系判定等情况。

2.双亲委派模型

虚拟机角度来说,有两种类加载器:

一种是启动类加载器,使用C++语言实现。

另一种就是所有其他的类加载器,这些类加载器都由java语言实现,独立于虚拟机外部,并且全部都继承自抽象类java.lang.ClassLoader。

按照开发人员的角度来看,绝大部分java程序都会使用到以下3种系统提供的类加载器:

启动类加载器:负责加载存放在%JAVA_HOME%\lib目录中的,或者通被-Xbootclasspath参数所指定的路径中的,并且被java虚拟机识别的(仅按照文件名识别,如rt.jar,名字不符合的类库,即使放在指定路径中也不会被加载)类库到虚拟机的内存中,启动类加载器无法被java程序直接引用

扩展类加载器:由sun.misc.Launcher$ExtClassLoader实现,负责加载%JAVA_HOME%\lib\ext目录中的,或者被java.ext.dirs系统变量所指定的路径中的所有类库,开发者可以直接使用扩展类加载器。

应用程序类加载器:由sun.misc.Launcher$AppClassLoader实现,负责加载用户类路径classpath上所指定的类库,是类加载器ClassLoader中的getSystemClassLoader()方法的返回值,开发者可以直接使用应用程序类加载器,如果程序中没有自定义过类加载器,该加载器就是程序中默认的类加载器。

类加载器之间的这种层次关系,称为类加载器的双亲委派模型。这里类加载器之间的父子关系一般不会以继承的关系来实现,而是使用组合关系来复用父加载器的代码。

//验证类加载器与类加载器间的父子关系
    public static void main(String[] args) throws Exception{
        //获取系统/应用类加载器
        ClassLoader appClassLoader = ClassLoader.getSystemClassLoader();
        System.out.println("系统/应用类加载器:" + appClassLoader);
        //获取系统/应用类加载器的父类加载器,得到扩展类加载器
        ClassLoader extcClassLoader = appClassLoader.getParent();
        System.out.println("扩展类加载器" + extcClassLoader);
        System.out.println("扩展类加载器的加载路径:" + System.getProperty("java.ext.dirs"));
        //获取扩展类加载器的父加载器,但因根类加载器并不是用Java实现的所以不能获取
        System.out.println("扩展类的父类加载器:" + extcClassLoader.getParent());
    }  

类加载器的双亲委派加载机制(重点):当一个类收到了类加载请求,他首先不会尝试自己去加载这个类,而是把这个请求委派给父类去完成,每一个层次类加载器都是如此,因此所有的加载请求都应该传送到启动类加载其中,只有当父类加载器反馈自己无法完成这个请求的时候(在它的加载路径下没有找到所需加载的Class),子类加载器才会尝试自己去加载。

使用双亲委派模型的好处:java类随着它的类加载器一起具备了一种带有优先级的层次关系,例如类java.lang.Object,它存放在rt.java之中,无论哪一个类要加载这个类,最终都是委派给处于模型最顶端的启动类加载器进行加载,因此Object在程序的各种类加载器环境中都是一个类。

双亲委派模型的源码实现:

主要体现在ClassLoader的loadClass()方法中,思路很简单:先检查是否已经被加载过,若没有加载则调用父类加载器的loadClass()方法,若父类加载器为空则默认使用启动类加载器作为父类加载器。如果父类加载器加载失败,抛出ClassNotFoundException异常后,调用自己的findClass()方法进行加载。

public Class<?> loadClass(String name) throws ClassNotFoundException {
        return loadClass(name, false);
    }
protected synchronized Class<?> loadClass(String name, boolean resolve)
    throws ClassNotFoundException
    {
    // First, check if the class has already been loaded
    Class c = findLoadedClass(name);
    if (c == null) {
        try {
        if (parent != null) {
            c = parent.loadClass(name, false);
        } else {
            c = findBootstrapClassOrNull(name);
        }
        } catch (ClassNotFoundException e) {
                // ClassNotFoundException thrown if class not found
                // from the non-null parent class loader
            }
            if (c == null) {
            // If still not found, then invoke findClass in order
            // to find the class.
            c = findClass(name);
        }
    }
    if (resolve) {
        resolveClass(c);
    }
    return c;
}  

下面看一个简单的双亲委派模型代码实例验证

public class ClassLoaderTest {
    public static void main(String[] args){
        //输出ClassLoaderText的类加载器名称
        System.out.println("ClassLoaderText类的加载器的名称:"+ClassLoaderTest.class.getClassLoader().getClass().getName());
        System.out.println("System类的加载器的名称:"+System.class.getClassLoader());
        System.out.println("List类的加载器的名称:"+List.class.getClassLoader());  

        ClassLoader cl = ClassLoaderTest.class.getClassLoader();
        while(cl != null){
                System.out.print(cl.getClass().getName()+"->");
                cl = cl.getParent();
        }
        System.out.println(cl);
}  
时间: 2024-10-16 02:10:02

虚拟机类加载机制--类加载器的相关文章

虚拟机类加载机制——类加载时机

由于道行不够深,所以此篇类加载机制的讲解主要来自于<深入理解Java虚拟机——JVM高级特性与最佳实践>的第7章 虚拟机类加载机制. 在前面<初识Java反射>中我们在开头提到要了解Java反射,就得要了解虚拟机的类加载机制.在这里,我们来试着窥探一下何为类加载. “虚拟机把描述类的数据从Class文件加载到内存,并对数据进行校验,类型的加载.连接和初始化,最终形成可以被虚拟机直接使用的Java类型,这就是虚拟机的类加载机制.”这句话确实读着好懂,但到底类加载做了什么呢?我们都知道

虚拟机类加载机制------类加载的过程

1.加载 虚拟机需要干三件事: ①.通过一个类的的全限定名来获取定义此类的二进制字节流(没有规定二进制字节流从那里获取,怎样获取,许多java技术也都建立在这基础上) ②将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构(将常量池转变成运行时常量池) ③在内存中生成一个代表这个类的java.lang.Class对象,作为方法区着各类的各种数据的访问入口. 相比较于类加载过程的其他阶段,非数组类获取类的二进制字节流的动作是开发人员可控性最强的,因为加载阶段既可以使用系统提供的引导类加载器

Jvm(56),虚拟机类加载机制----类加载的过程----初始化

类初始化阶段是类加载过程的最后一步,前面的类加载过程中,除了在加载阶段用户应用程序可以通过自定义类加载器参与之外,其余动作完全由虚拟机主导和控制.到了初始化阶段,才真正开始执行类中定义的Java程序代码(或者说是字节码). 在准备阶段,变量已经赋过一次系统要求的初始值,而在初始化阶段,则根据程序员通过程序制定的主观计划去初始化类变量和其他资源,或者可以从另外一个角度来表达:初始化阶段是 执行类构造器<clinit>()方法的过程.我们在下文会讲解<clinit>()方法是怎么生成的

Jvm(55),虚拟机类加载机制----类加载的过程----解析

解析阶段是虚拟机将常量池内的符号引用替换为直接引用的过程,符号引用在前一章讲解 Class文件格式的时候已经出现过多次,在Class文件中它以CONSTANT_Class_info. CONSTANT_Fieldref_info.CONSTANT_Methodref_info等类型的常量出现,那解析阶段中所说的直接引用与符号引用又有什么关联呢? 符号引用(Symbolic References):符号引用以一组符号来描述所引用的目标,符号可以是任何形式的字面量,只要使用时能无歧义地定位到目标即可

Jvm(51),虚拟机类加载机制----类加载的时机

在了解下面的举的例子之前我们先来了解一下类的加载顺序? 1 public class test1 { 2 public static void main(String[] args) { 3 C c = new C(); 4 } 5 } 6 7 class A{ 8 int a = 0; 9 Method m = new Method(a); 10 static int a1 = 10; 11 static{ 12 System.out.println("A:执行静态代码块A"+a1)

JVM类加载机制---类加载的过程

一.类加载的时机 类从被加载到虚拟机内存中开始,到卸载出内存为止,它的整个生命周期包括:加载.验证.准备.解析.初始化.使用.卸载 7个阶段,其中验证.准备.解析 3个部分统称为 连接. 二.具体步骤解析 1.加载 加载阶段,虚拟机要完成以下3件事情: 1)通过一个类的全限定名来获取定义此类的二进制字节流: 2)将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构: 3)在内存中生成一个代表这个类的java.lang.class对象,作为方法区这个类的各种数据的访问入口. 相对于类加载过

Java虚拟机 - 类加载机制

[深入Java虚拟机]之四:类加载机制 类加载过程     类从被加载到虚拟机内存中开始,到卸载出内存为止,它的整个生命周期包括:加载.验证.准备.解析.初始化.使用和卸载七个阶段.它们开始的顺序如下图所示: 其中类加载的过程包括了加载.验证.准备.解析.初始化五个阶段.在这五个阶段中,加载.验证.准备和初始化这四个阶段发生的顺序是确定的,而解析阶段则不一定,它在某些情况下可以在初始化阶段之后开始,这是为了支持Java语言的运行时绑定(也成为动态绑定或晚期绑定).另外注意这里的几个阶段是按顺序开

Java 虚拟机程序执行:02 虚拟机的类加载机制

虚拟机的类加载机制 虚拟机的类加载机制 类加载的时机 类的显式加载和隐式加载 类加载的过程 类的生命周期 加载 加载的 3 个阶段 分类 验证 准备 解析 初始化 类加载器 如何判断两个类 “相等” 类加载器的分类 双亲委派模型 类加载的时机 JVM 会在程序第一次主动引用类的时候,加载该类,被动引用时并不会引发类加载的操作.也就是说,JVM 并不是在一开始就把一个程序就所有的类都加载到内存中,而是到不得不用的时候才把它加载进来,而且只加载一次.那么什么是主动引用,什么是被动引用呢? 主动引用

一文读懂Java类加载机制

Java 类加载机制 Java 类加载机制详解. @pdai Java 类加载机制 类的生命周期 类的加载:查找并加载类的二进制数据 连接 验证:确保被加载的类的正确性 准备:为类的静态变量分配内存,并将其初始化为默认值 解析:把类中的符号引用转换为直接引用 初始化 使用 卸载 类加载器, JVM类加载机制 类加载器的层次 寻找类加载器 类的加载 JVM类加载机制 自定义类加载器 参考文章 类的生命周期 其中类加载的过程包括了加载.验证.准备.解析.初始化五个阶段.在这五个阶段中,加载.验证.准