JVM类加载器ClassLoader解读

ClassLoader类加载器负责将类加载进入JVM中。

ClassLoader的作用

(1)加载class文件进入JVM

(2)审查每个类应该由谁加载,采用双亲委托机制

(3)将class字节码重新解析成JVM要求的对象格式

ClassLoader结构分析

protected final Class<?> defineClass(byte[] b, int off, int len)throws ClassFormatError{
     return defineClass(null, b, off, len, null);
  }
protected Class<?> findClass(String name) throws ClassNotFoundException {
        throw new ClassNotFoundException(name);
    }
public Class<?> loadClass(String name) throws ClassNotFoundException {
        return loadClass(name, false);
    }
protected final void resolveClass(Class<?> c) {
        resolveClass0(c);
    }

(1)defineClass方法用来将byte字节流解析成JVM能够识别的Class对象。如果直接用此方法创建类的class对象,这个类的class对象还没有resolve,这个resolve将会在这个对象真正实例化时才能进行,通常这个方法要和defineClass方法一起使用。如果想在类被加载到JVM时就被链接,可以调用resolveClass方法

(2)ClassLoader类是一个抽象类,如果我们要实现自己的加载器一般会继承URLClassLoader这个子类。

ClassLoader的双亲委派加载机制

双亲委派加载机制即如果一类的加载器收到一个加载请求,它首先会将这个请求委托给父类加载器去加载,如果父类判断没有加载过这个类,在继续向其上一层加载器委派,每一层次的类加载器都是如此,则所有的类加载请求最终都会到达顶层的类加载器,只有父类加载器无法加载这个请求,子类才会去加载此类,这样可以避免重复加载同时防止用户自定义的类加载器替代java核心加载器。

JVM中提供的三个ClassLoader类,下图示层次

(1)Bootstrap ClassLoader:主要加载JVM自身工作需要的类,完全由JVM自己控制,这个类不遵守双亲委派加载机制,它仅仅是一个类的加载工具,既没有父加载器也没有子加载器。

(2)ExtClassLoader:这个类本身是JVM自身的一部分,但不是由JVM自身实现的,服务的特定目标在java.ext.dirs目录下的类

(3)AppClassLoader:这个类服务java.class.path目录下的类,即classpath路径。

  如果我们要实现自己的类加载器,不管是直接继承ClassLoader还是继承URLclassLoaderlei ,它的父加载器都是AppClassLoader,因为不管调用哪个父类构造器,创建的对象都必须最终调用getSystemClassLoader()作为父类加载器,而该方法获取的正是AppClassLoader。
  如果应用中没有定义其他的类加载器,那么除了java.ext.dirs下的类是由ExtClassLoader来加载,其他的都是由AppClassLoader来加载。

JVM加载Class文件到内存的两种方式

(1)隐式加载:在代码中不通过调用ClassLoader来加载需要的的类,而是通过JVM自动加载需要的类到内存方式。如我们类继承或类中引用其他类的时候,JVM在解析当前这个类的时候发现引用类不在内存中就会去自动的将这些类加载进入到内存。

(2)显示加载:在代码中通过调用ClassLoader类来加载类。如this.getClass().getClassLoader().loadClass()或者Class.forName()或者我们自己实现的findClass()方法。

类加载过程

类的生命周期

下面是类加载的过程

(1)第一阶段:找到class文件并把这个文件包含的字节码加载到内存

(2)第二阶段:字节码验证,class类数据结构分析以及相应内存分配,符号表的链接。

(3)第三阶段:类中静态属性初始化赋值以及静态代码块的执行

加载字节码到内存

在加载阶段JVM需要做的三件事:

(1)通过一个类的全限定名来获取定义此类的二进制字节流

(2)将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构

(3)在java堆中生成一个代表这个类的java.lang.Class对象,作为方法区这些数据的访问入口

在抽象类ClassLoader中没有定义如何去加载,如何去找到指定类并加载到内存中需要子类中去实现,即实现findClass()方法。如在URLClassLoader中通过一个URLClassPath类帮组取得要加载的class文件字节流,而这个URLClassPath定义了到哪里去找这个class文件,如果找到这个class文件,再读取byte字节流通过调用defineClass方法来创建类对象。

 protected Class<?> findClass(final String name)
         throws ClassNotFoundException
    {
        try {
            return AccessController.doPrivileged(
                new PrivilegedExceptionAction<Class>() {
                    public Class run() throws ClassNotFoundException {
                        String path = name.replace(‘.‘, ‘/‘).concat(".class");
                        Resource res = ucp.getResource(path, false);
                        if (res != null) {
                            try {
                                return defineClass(name, res);
                            } catch (IOException e) {
                                throw new ClassNotFoundException(name, e);
                            }
                        } else {
                            throw new ClassNotFoundException(name);
                        }
                    }
                }, acc);
        } catch (java.security.PrivilegedActionException pae) {
            throw (ClassNotFoundException) pae.getException();
        }
    }

验证阶段

    验证是连接的第一步,这一阶段是为了确保Class文件的字节流中包含的信息符合当前虚拟机的要求,并且不会危害虚拟机自身的安全。验证阶段对虚拟机非常重要,但不一定是必要阶段。如果所运行的全部代码都已经反复验证过了,在实施阶段可以关闭大部分类的验证,以缩短虚拟机类加载的时间。

(1)第一步:文件格式验证,要验证字节流是否符合Class文件格式规范,并且能被虚拟机处理。例如,是否已魔数0xCAFEBABE

(2)第二步:元数据验证,对字节码描述的信息进行语义分析,以保证其描述信息符合java语言规范要求。例如这个类是否有父类,这个类的父类是否继承了不允许被继承的类。

(3)第三步:字节码验证,整个验证阶段最复杂的阶段,主要是进行数据流和控制流分析,保证被校验类的方法在运行时不会做出危害虚拟机安全的行为。

(4)第四步:符号引用验证, 这一步发生在虚拟机将符号引用转换为直接引用阶段,即在解析中发生。

准备阶段

   准备阶段是正式为类变量(被static修饰的变量)分配内存并设置变量初始值的阶段,这些内存都将在方法区中进行分配。这里仅仅包括类变量的内存分配,不包括实例变量,实例变量的内存分配将会在对象实例化时随着对象一起分配在java堆中。这里的初始值都是零值,如下代码中flag的初始值是false。
  public static boolean flag=true;
 但是如果上述代码变成下面这样,那么在准备阶段变量flag就会被初始化为ConstantValue属性所指定的值,也就是true。
public static final boolean flag=true;

解析阶段

解析阶段是虚拟机将常量池内的符号引用替换为直接引用的过程。解析动作主要针对的是类或接口,字段,类方法,接口方法四类符号引用。

  • 符号引用:符号引用以一组符号来描述所引用的目标,符号可以使任何形式的字面量,只要使用时能无歧义地定位到目标即可。符号引用与内存布局无关。引用的目标并不一定已经加载到内存中。
  • 直接引用:直接引用可以使直接指向目标的指针,相对偏移量或是一个能间接定位到目标的句柄。直接引用是与虚拟机实现的内存布局有关的,同一个符号引用在不同虚拟机上翻译出来的直接引用一般不会相同的。如果有了直接引用,那引用的目标必定已经子啊内存中存在。

    初始化阶段

初始化化阶段是类加载过程中最后一个阶段,前面的阶段除了类加载阶段用户可以通过自定义的加载器参与外,其余动作完全由虚拟机来主导和控制的,到了初始化阶段才真正开始执行类中定义的java程序代码。

这个阶段是根据程序员通过程序制定的主观计划去初始化类变量和其他资源,类变量被初始化为默认值,静态代码块获得执行。

声明:本文很多是对《深入分析javaweb技术内幕》和《深入理解java虚拟机》的总结,希望大家多多提出意见,共同进步

版权声明:本文为博主原创文章,未经博主允许不得转载。

时间: 2024-10-27 05:44:49

JVM类加载器ClassLoader解读的相关文章

jvm之java类加载机制和类加载器(ClassLoader)的详解

当程序主动使用某个类时,如果该类还未被加载到内存中,则JVM会通过加载.连接.初始化3个步骤来对该类进行初始化.如果没有意外,JVM将会连续完成3个步骤,所以有时也把这个3个步骤统称为类加载或类初始化. 一.类加载过程 1.加载 加载指的是将类的class文件读入到内存,并为之创建一个java.lang.Class对象,也就是说,当程序中使用任何类时,系统都会为之建立一个java.lang.Class对象. 类的加载由类加载器完成,类加载器通常由JVM提供,这些类加载器也是前面所有程序运行的基础

java类加载器——ClassLoader

Java的设计初衷是主要面向嵌入式领域,对于自定义的一些类,考虑使用依需求加载原则,即在程序使用到时才加载类,节省内存消耗,这时即可通过类加载器来动态加载. 如果你平时只是做web开发,那应该很少会跟类加载器打交道,但如果你想深入学习tomcat服务器的架构,它是必不可少的.所谓类加载器,就是用于加载Java类到Java虚拟机中,它负责读取Java字节码,并转换成java.lang.Class类的一个实例,使字节代码.class文件得以运行.一般类加载器负责根据一个指定的类找到对应的字节代码,然

浅析java类加载器ClassLoader

作为一枚java猿,了解类加载器是有必要的,无论是针对面试还是自我学习. 本文从JDK提供的ClassLoader.委托模型以及如何编写自定义的ClassLoader三方面对ClassLoader做一个简要的总结. JDK中提供的ClassLoader 1. Bootstrap ClassLoader Bootstrap加载器是用C++语言写的,它是在Java虚拟机启动后初始化的,它主要负责加载%JAVA_HOME%/jre/lib以及%JAVA_HOME%/jre/classes中的类,是最顶

深入理解 java类加载器ClassLoader

类加载器(class loader)用来加载 Java 类到 Java 虚拟机中.一般来说,Java 虚拟机使用 Java 类的方式如下:Java 源程序(.java 文件)在经过 Java 编译器编译之后就被转换成 Java 字节代码(.class 文件).类加载器负责读取 Java 字节代码,并转换成java.lang.Class类的一个实例.每个这样的实例用来表示一个 Java 类.通过此实例的 newInstance()方法就可以创建出该类的一个对象,也就是万能的Class对象. Jav

JAVA之了解类加载器Classloader

1.类的加载.连接和初始化   类初始化通常包括加载.连接.初始化三个步骤. (1)进程的结束 每当运行一个java程序时,将会启动一个java虚拟机进程,不管程序多么复杂,有多少线程,都在这个java虚拟机进程里.以下四种情况会使得该进程被终止-- 程序运行到最后正常结束: 程序里遭遇了System.exit(),或者是Runtime.getRunTime().exit()代码: 程序执行中遇到了未捕获的异常或者错误: java所在平台强制结束了JVM进程: 当该进程结束,那么该进程在内存中的

类加载器ClassLoader的理解

最近在做一个热加载Class的小组件,这个组件需要对类加载器ClassLoader有所了解,我就顺便借这个机会把学到的一点皮毛与大家分享一下. 从Class文件开始 ClassLoader,顾名思义就是类加载器.简单的说就是把Class文件加载到JVM中,之后程序就能正常的运行了. 我们平时写的代码都是.java格式的文件,但是这个文件是不能够直接运行的.比如下面这个简单的测试程序Test.java 1 public class Test { 2 3 public static void mai

java的类加载器ClassLoader

类在执行之前会执行三个步骤:加载 ->   连接  -> 初始化 1.java中类的加载 java 类的加载指的是将类的.class文件中的二进制数据读入到内存中,将其放在运行时数据区的方法区内,然后在堆区创建一个Class对象,用来封装类在方法区的数据结构.可以把堆区的Class理解为方法区的一面镜子,对应方法区的类的数据结构,通过这面镜子实现类的调用. 加载.class文件的多种方式: 1.从本地系统中直接加载 2.通过网络下载.class文件 3.从zip,jar里加载.class文件

(二十七)JVM类加载器机制与类加载过程

一.Java虚拟机启动.加载类过程分析 下面我将定义一个非常简单的java程序并运行它,来逐步分析java虚拟机启动的过程. package org.luanlouis.jvm.load; import sun.security.pkcs11.P11Util; /** * Created by louis on 2016/1/16. */ public class Main{ public static void main(String[] args) { System.out.println(

Java类加载器( CLassLoader ) 死磕 3: 揭秘 ClassLoader抽象基类

[正文]Java类加载器(  CLassLoader ) 死磕3:  揭秘 ClassLoader抽象基类 3.1. 揭秘ClassLoader抽象基类 3.1.1. 类的加载分类:隐式加载和显示加载 java中类是动态加载的,jvm启动的时候,并不会一次性加载所有的class文件,而是根据需要去动态加载.一是加快启动的速度,二是节约内存.如果一次性加载全部jar包的所有class,速度会很慢. 动态载入一个class类,有两种方式: (1) implicit隐式加载 即通过实例化才载入的特性来