【深入理解JVM】ClassLoader类加载机制

Java程序并不是一个原生的可执行文件,而是由许多独立的类文件组成,每一个文件对应一个Java类。此外,这些类文件并非立即全部装入内存的,而是根据程序需要装入内存。ClassLoader专门负责类文件装入到内存。

在class文件中描述的各种信息,最终都需要被加载到虚拟机中之后,才能被运行和使用。虚拟机把描述类的数据从class文件加载到内存,并对数据进行校验,转换,解析和初始化,最终形成可以被虚拟机直接使用的Java类型,这就是虚拟机的类加载机制

总结起来ClassLoader的作用:

(1)加载class文件进入JVM
(2)审查每个类应该由谁加载,采用双亲委托机制
(3)将class字节码重新解析成JVM要求的对象格式

1、JVM的四种类加载器

类加载器:通过一个类的全限定名来获取描述此类的二进制字节流,实现这个动作的代码模块称为“类加载器”

这里写图片描述

从上图我们就可以看出类加载器之间的父子关系(注意不是类的集继承关系)。

Bootstrap ClassLoader:BootStrap 是最顶层的类加载器,它是由C++编写并且已经内嵌到JVM中了,主要用来读取Java的核心类库JRE/lib/rt.jar。

Extension ClassLoader:负责加载java平台中扩展功能的一些jar包,包括$JAVA_HOME中jre/lib/*.jar或-Djava.ext.dirs指定目录下的jar包。
App ClassLoader(System ClassLoader):负责记载classpath中指定的jar包及目录中class。

Custom ClassLoader:属于应用程序根据自身需要自定义的ClassLoader,如tomcat、jboss都会根据j2ee规范自行实现ClassLoader。

这里写图片描述

如果我们要实现自己的类加载器,不管是直接继承ClassLoader还是继承URLclassLoaderlei ,它的父加载器都是AppClassLoader,因为不管调用哪个父类构造器,创建的对象都必须最终调用getSystemClassLoader()作为父类加载器,而该方法获取的正是AppClassLoader。

如果应用中没有定义其他的类加载器,那么除了java.ext.dirs下的类是由ExtClassLoader来加载,其他的都是由AppClassLoader来加载。

下面问题就来了,JVM有四种类型的类加载器,java是如何区分一个类该由哪个类加载器来完成呢

在这里java采用了双亲委托机制,这个机制简单来讲,就是“类装载器有载入类的需求时,会先请示其Parent使用其搜索路径帮忙载入,如果Parent 找不到,那么才由自己依照自己的搜索路径搜索类”。具体流程如下:

1、”A类加载器”加载类时,先判断该类是否已经加载过了;

2、如果还未被加载,则首先委托其”A类加载器”的”父类加载器”去加载该类,这是一个向上不断搜索的过程,当A类所有的”父类加载器”(包括bootstrap classloader)都没有加载该类,则回到发起者”A类加载器”去加载;

3、如果还加载不了,则抛出ClassNotFoundException。

2、ClassLoader抽象类的几个关键方法

loadClass()
此方法负责加载指定名字的类,ClassLoader的实现方法为先从已经加载的类中寻找,如没有则继续从parent ClassLoader中寻找,如仍然没找到,则从System ClassLoader中寻找,最后再调用findClass方法来寻找,如要改变类的加载顺序,则可覆盖此方法

findLoadedClass()
此方法负责从当前ClassLoader实例对象的缓存中寻找已加载的类,调用的为native的方法。

findClass()
此方法直接抛出ClassNotFoundException,因此需要通过覆盖loadClass或此方法来以自定义的方式加载相应的类。

findSystemClass()
此方法负责从SystemClassLoader中寻找类,如未找到,则继续从Bootstrap ClassLoader中寻找,如仍然为找到,则返回null。

defineClass()
此方法负责将二进制的字节码转换为Class对象

resolveClass()
此方法负责完成Class对象的链接,如已链接过,则会直接返回。

Class.forName()与ClassLoader.loadClass()
这两方法都可以通过一个给定的类名去定位和加载这个类名对应的 java.lang.Class 类对象,区别如下:

  1. 初始化
    Class.forName()会对类初始化,而loadClass()只会装载或链接。ClassLoader.loadClass()加载的类对象是在第一次被调用时才进行初始化的。可以利用上述的差异。比如,要加载一个静态初始化开销很大的类,就可以选择提前加载该类(以确保它在classpath下),但不进行初始化,直到第一次使用该类的域或方法时才进行初始化。

  2. 类加载器可能不同
    Class.forName(String)方法(只有一个参数),使用调用者的类加载器来加载, 也就是用加载了调用forName方法的代码的那个类加载器。当然,它也有个重载的方法,可以指定加载器。相应的,ClassLoader.loadClass()方法是一个实例方法(非静态方法)调用时需要自己指定类加载器,那么这个类加载器就可能是也可能不是加载调用代码的类加载器(调用代码类加载器通getClassLoader0()获得)

3、类的加载过程

类的加载要经过三步:装载(Load),链接(Link),初始化(Initializ)。其中链接又可分为校验(Verify),准备(Prepare),解析(Resolve)三步。
这里写图片描述

三个阶段

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

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

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

这里写图片描述
总结起来,类从被加载到虚拟机内存开始,到卸载出内存为止,其生命周期包括:加载(loading),验证(verification),准备(preparation),解析(resolution),初始化(initialization)ÿ

  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值