虚拟机类加载器

来源:互联网 发布:淘宝hd登录失败 编辑:程序博客网 时间:2024/06/06 09:31
在虚拟机中,有一个重要的角色,承担了字节码加载阶段的工作,它就是ClassLoader,类加载器。类加载器的主要作用使从外部系统获得Class二进制数据流,下面一起了解下类加载器的原理以及类加载器是如何把字节码加载到虚拟机的。
一.认识ClassLoader
ClassLoader是Java的核心组件,所有的class都是由ClassLoader进行加载的,ClassLoader负责通过各种方式将Class信息的二进制数据流读入系统,然后交给Java虚拟机进行连接,初始化等操作。因此,ClassLoader在整个装载阶段,只能影响到类的加载,无法通过ClassLoader去改变类的连接和初始化行为。
二.ClassLoader的分类
在标准的Java程序中,Java虚拟机会创建3类ClassLoader为整个应用程序服务,他们分别是:BootStrap ClassLoader(启动类加载器),Extension ClassLoader(扩展类加载器)和Application ClassLoader(应用类加载器)。此外,应用程序还可以拥有自定义的ClassLoader,扩展虚拟机获取Class数据的能力。
各个ClassLoader是分层的,自顶向下为启动类加载器,扩展类加载器,应用类加载器和自定义类加载器。其中,应用类加载器的双亲为扩展类加载器,扩展类加载器的双亲为启动类加载器。当系统需要使用一个类时,在判断类是否已经被加载时,会先从当前底层类加载器进行判断。当系统需要加载一个类时,会从顶层类开始加载,依次向下尝试,知道成功。
在这些ClassLoader中,启动类加载器最为特别,它是完全由C代码实现的,并且在Java中没有对象与之对应。系统的核心类都是由启动类加载器进行加载的,它也是虚拟机的核心组件。扩展类加载器和应用类加载器都有对应的Java对象可供使用。我们无法在Java代码中访问启动类加载器,当试图获得一个类的ClassLoader时,如果得到的是null,并不意味着没有加载器为它服务,而是指加载那个类的为启动类加载器。
在虚拟机设计中,使用这种分散的ClassLoader去加载类的好处是:不同层次的类可以由不同的ClassLoader加载,从而进行划分,有助于系统的模块化设计。一般来说,启动类加载器负责加载系统的核心类,比如rt.jar中的Java类;扩展类加载器用于加载lib/ext/*.jar中的Java类;应用类加载器用于加载用户类,也就是用户程序的类;自定义类加载器用于加载一些特殊途径的类,一般也是用户程序类。
三.ClassLoader的双亲委托模型
ClassLoader设计中的一个很重要的思想就是双亲委托模型,即在类加载的时候,系统会判断当前类是否已经被加载,如果已经被加载,就会直接返回可用的类,否则就会尝试加载,在尝试加载时,会先请求双亲处理,如果双亲请求失败,则会自己加载。这里我有疑问的是为什么要叫双亲,这明明就是一个父类呀,有没有母亲,为什么叫双亲......
如果双亲为null代表有两种情况,一种是其双亲为启动类加载器,一种是当前加载器就是启动类加载器
使用双亲委托模型来组织类加载器之间的关系,有一个显而易见的好处就是Java类随着它的类加载器一起举杯了一种带有优先级的层次关系。例如类java.lang.Object,它存放在rt.jar中,无论哪一个类加载器要加载这个类,最终都是委托给处于模型最顶端的启动类加载器进行加载,因此Object类在程序的各种类加载器环境中都是一个类。相反,如果没有使用双亲委托模型,由各个类加载器自行去加载的话,如果用户编写了一个称为java.lang.Object的类,并放在程序的classPath中,那么系统中将会出现多个不同的Object类,Java类型体系中最基础的行为也就无法保证,应用程序也将会变得一片混乱。
四.双亲委托模式的弊端
双亲委托模式的一个比较大的弊端是顶层的ClassLoader无法访问底层的ClassLoader所加载的类,只能是底层的ClassLoader访问顶层ClassLoader加载的类。
在Java平台中,把核心类中提供外部服务,可由应用层自行实现的接口,通常可称为Service Provider Interface,即SPI。
为了解决双亲委托模式的这个问题,Java设计团队引入了一个不太优雅的扩展,引入线程上下文类加载器。这个类加载器可由通过java.lang.Thread类的setContextClassLoader()方法进行设置,如果创建线程时还未设置,它将会从父线程中继承一个,如果在应用程序的全局范围内都没有设置过的话,那这个类加载器默认就是应用类加载器。这样上层类加载器可以通过getContextClassLoader()方法来获取下层的类加载器了。
五.热替换的实现
热替换是指在程序的运行过程中,不停止服务,只通过替换程序文件来修改程序的行为。热替换的关键需求在于服务不能中断,修改必须立即表现在正在运行的系统之中。对于Java来说,如果一个类已经加载到系统中,通过修改类文件,并无法让系统再来加载并重定义是这个类。因此,在Java中实现这一功能的一个可行的方法就是灵活运用ClassLoader。
在这里我们需要理解,由不同ClassLoader加载的同名类属于不同的类型,不能相互转化和兼容。这也意味着,判断两个类是否相等,还要求是同一个类加载器加载才可能相等。两个不同ClassLoader加载同一个类,在虚拟机内部,会认为两个类是完全不同的。利用这个特点,可以用来模拟热替换的实现,基本思路是,创建自定义ClassLoader实例,加载需要热替换的类,创建新的ClassLoader实例创建类的对象,被新的ClassLoader加载,已经成为了新的类型,运行新对象的方法。这样就实现了一个热替换操作。即用新加载的类来代替原来虚拟机已经加载的类来执行操作。
原创粉丝点击