JVM之类加载器


概述


  • 我们知道我们的源码得先编译为二进制的class文件,才能被虚拟机识别接收。但虚拟机具体是如何加载这些class文件进入虚拟机内存并使用的呢?首先就是依靠“类加载器”进行加载的。
  • 在java语言中,类型的加载和连接过程都是在程序运行时期完成的,这样会在类加载时增加一些性能开销,但是却能为java应用提供高度的灵活性。因为java的类加载机制是在运行期间动态加载和动态连接所以java天生就有非常好的动态扩展性。比如项目中并不需要停止你就可以为该项目添加新的功能或修改功能实现等等,最常见的jsp的热部署,你修改了jsp文件虚拟机立马就可以识别并更改内容。
  • 类加载器最初是为了满足Java Applet的需求而被开发出来的,如今Java Applet技术基本已经死掉了,但类加载器却在类层次划分,OSGI、热部署、代码加密等领域大放异彩,成为了Java体系中一块重要的基石。

类加载器是什么


  • 类加载器本质上也是一个类,并继承于抽象类java.lang.ClassLoader或其子类,除了bootstrap 类加载器。
  • 类加载器理论上可以有无数多个,因为虽然jdk中有提供几个级别比较高类加载器,但是用户可以自定类加载器,显示的实现类的加载。
  • 前面说了类加载器本质上也是一个类,那类加载器需不需要被加载呢?当然需要。那问题来了,如果类加载器一样需要被加载,那虚拟机是如何开始的,它的“”在哪?那就是bootstrap 类加载器
  • bootstrap 类加载器是C++写的程序,可以独立运行,可以说是JVM的运行起点,而且java的运行环境所需要的所有类库,都是由它来装载。在Bootstrap完成它的任务后,会生成 一个AppClassLoader(实际上之前系统还会使用扩展类装载器ExtClassLoader,它用于装载Java运行环境扩展包中的类),这个 类装载器才是我们经常使用的,可以调用ClassLoader.getSystemClassLoader() 来获得。bootstrap 类加载器可以说是非常重要的存在,我们需要知道它的存在,因为但凡能由 bootstrap 类加载器加载的类都是由它加载的(涉及到类加载机制的问题),可以说是无处不在。但我们也不需要过分深究,因为在java代码中你完全无法接触到 bootstrap 类加载器。

类加载机制之双亲委派模型


  • 对于类加载器我们可以按照加载器的目的先进行一定的划分,分为启动类加载器,扩展类加载器,应用程序类加载器。这三种是系统提供的,根据业务需求我们还可以在应用程序类加载器下面在添加自己自定义的加载器。首先我们先介绍一下3个系统提供的类加载器:

    • 启动类加载器(Bootstrap ClassLoader):上文已经有过详细的介绍了,boostrap类加载器主要是加载<JAVA_HOME>\lib目录中的类,或者是参数-Xbootclasspath所指定路径的类,但是这些类必须是虚拟机识别的类库(仅按照文件名识别,如rt.jar)才会由boostrap类加载器加载。
    • 扩展类加载器(Extension ClassLoader):这个加载器是由sum.misc.Launcher$ExtClassLoader实现的,它负责加载<JAVA_HOME>\lib\ext目录中的类,或者是被系统变量java.ext.dirs所指定路径的类库,用户可以直接调用该类来加载类。
    • 应用程序类加载器(Application ClassLoader):这个加载器是由sum.misc.Launcher$AppClassLoader实现的。并继承于扩展类加载器。由于这个类是ClassLoader.getSystemClassLoader()的返回对象类型,所以这个类也被称为系统类加载器,它负载加载用户路径(classpath,是不是很熟)上所指定的类库。所以如果你没有自定义类加载器,一般你定义的类都由该类加载器进行加载。
  • 所以说了这么多什么是双亲委派模型?如图1,当一个(B)类加载器加载(C)类时,首先执行一次检查,查看客户程序请求的(C)类是否已经装入。如果是,则返回已装入的(C)类,请求处理完毕。如果不是则将加载(C)类的请求委托给父类加载器执行,并层层传递上去直到bootstrap类加载器。然后再从bootstrap类加载器开始,如果这个父类加载器能加载(C)类,则由父类加载加载,否则返回给子类加载器进行加载。如果最终到达(B)类加载器(发起请求的类加载器)也无法加载(C)类,则抛出相应的异常,根据不同情况有好几种异常。

没有

图1
  • 类加载器之间的这种层次关系,就曾为双亲委派模型,双亲委派模型除了Bootstrap类加载器,其它类加载器都要有父类加载器。这里的类加载器之间的父子关系一般不是以继承的关系来实现,而都是使用组合的关系来复用父加载器的代码。excuse me?不理解什么意思,待补。
  • 双亲委派模型并不是一个强制性的约束,而是推荐给开发者们一个类加载器实现方式。实际上双亲委派模型也被多次破坏,现在类加载器的模型应该是在双亲委派模型上的一种网状模型,类加载器可以直接指定其它加载器进行加载,这在后边会在提到。但是双亲委派模型依然很重要,依然是类加载器之间关系的基础。
  • 双亲委派模型的好处就是,同一个类能保证一定是由同一个类加载器进行加载的。比如rt.jar包里的java.lang.Object类,无论你用哪一个类加载器去加载它,最终都还是由Bootstrap类加载器进行加载。它使得java的类同它的类加载器一起具备了一种优先级的关系。另外判断两个类是否为同一个类的一个必要条件就是它是否是由同一个类加载器进行加载的。比如你引用了两个jar包,这两个jar包都依赖与rt.jar包,如果没有双亲委派模型,它们可能就会被各自的类加载器进行加载,就会出现两个java.lang.Object类,java类型体系中最基础的行为也无法保证。

  • 感觉写的很杂,很多不到位的地方,等我把其它相关内容都写好后再进行整理。
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值