Java虚拟机札记-类加载机制

虚拟机把描述类的数据从Class文件加载到内存,并对数据进行校验、解析、初始化,最终形成可以被虚拟机直接使用的Java类型,这就是虚拟机的类加载机制。虚拟机如何加载Class文件?Class文件文件进入内存后是如何形成Java类型的?这就是本文要介绍的内容。

在Java语言中,类的加载、连接、初始化都是在程序运行期间完成的,而不是在编译期间完成。这种策略虽然会在类加载时增加一些性能开销,但却大大提高了Java的灵活性。Java的可以动态扩展的语言特性就是依赖运行期间动态加载连接实现的。比如Applet、JSP都是使用了Java运行期间加载类的特性。

类加载的过程

类从被加载到虚拟机内存中开始,到卸载出内存位置,它的生命周期一共包括7个阶段:加载、验证、准备、解析、初始化、使用、卸载。其中验证、准备、解析三个阶段统称为连接。
MarkdownPhotos/master/CSDNBlogs/JVM/lifecycleOfClass.png

类加载器

在“类加载”过程中的第一个阶段“加载”中有这么一个步骤:通过一个类的全限定名来获取描述此类的二进制字节流,实现这个步骤的代码称为类加载器。虚拟机设计团队将类加载器放在Java虚拟机外部去实现,以便让应用程序自己去决定如何获取所需要的类。

类与类加载器

对于任意一个类,都需要由加载它的类加载器和这个类本身一同确立其在Java虚拟机中的唯一性。即使两个类来源于同一个Class文件,被同一个虚拟机加载,只要加载它们的类加载器不同,这两个类就必定不相等。

类加载器种类

从虚拟机的角度看,类加载器有两种

  • 一种是Java虚拟机自身的启动类加载器,这个类加载器使用C++实现;
  • 另一种即使其他的所有类加载器,这些类加载器使用Java语言实现,独立于虚拟机外部,继承于java.lang.ClassLoader。

从Java开发人员的角度看,绝大部分Java程序都是由以下三种系统提供的类加载器相互配合进行加载的,如有需要,还可以加入自定义类加载器。

  • 启动类加载器(Bootstrap ClassLoader)
  • 扩展类加载器(Extention ClassLoader)
  • 应用程序类加载器(Application ClassLoader)

启动类加载器
实现:就是前面介绍的Java虚拟机自身的启动类加载器,由c++实现。
职责:负责将存放在<JAVA_HOME>\lib目录中,或者被-Xbootclasspath参数指定的路径中,且被Java虚拟机识别的类库加载到虚拟机中。
特点:无法被Java程序直接引用。

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

应用程序类加载器
实现:由sun.misc.Launcher$AppClassLoader实现
职责:负责加载用户类路径(ClassPath)指定的类库。
特点:开发者可以直接使用。
别名:系统类加载器。因为通过ClassLoader中的getSystemClassLoader()方法可以获取这个类加载器。如果应用程序中没有定义过自己的类加载器,那么这就是默认的类加载器。

双亲委派模型

类加载器之间的一般关系如图所示
MarkdownPhotos/master/CSDNBlogs/JVM/Parents-Delegation-Model.png
图中所展示的类加载器之间的关系,称为类加载器的双亲委派模型(Parents Delegation Model)。双亲委派模型要求除了顶层的启动类加载器外,其余加载器都应当有自己的父类加载器。类加载器之间的父子关系一般通过组合关系实现,而不是子加载器继承父加载器。

工作过程
如果一个类加载器收到了类加载的请求,它首先不会自己去尝试加载这个类,而是把这个请求委派给父类加载器完成。每个层次的类加载器都是如此,因此所有的加载请求最终都应该传送到顶层的启动类加载器中,只有到父加载器反馈自己无法完成这个加载请求(它的搜索范围没有找到所需的类)时,子加载器才会尝试自己去加载。

优点
Java类随着它的类加载器一起具备了一种带优先级的层次关系。比如java.lang.Object,它存放在rt.jar中,无论哪个类加载器要加载这个类,最终都是委派给启动类加载器进行加载,因此Object类在程序的各个类加载器环境中,都是同一个类。如果没有使用双亲委派模型,让各个类加载器自己去加载,那么Java类型体系中最基础的行为也得不到保障,应用程序会变得一片混乱。

破坏双亲委派模型

待补充

自定义类加载器

为什么要自定义类加载器?
让应用程序自己去决定如何获取所需要的类。

因为虽然Java中给用户提供了很多类加载器,但是和实际使用比起来,功能还是匮乏。举一个例子来说吧,主流的Java Web服务器,比如Tomcat,都实现了自定义的类加载器(一般都不止一个)。因为一个功能健全的Web服务器,要解决如下几个问题:
1、部署在同一个服务器上的两个Web应用程序所使用的Java类库可以实现相互隔离。这是最基本的要求,两个不同的应用程序可能会依赖同一个第三方类库的不同版本,不能要求一个类库在一个服务器中只有一份,服务器应当保证两个应用程序的类库可以互相使用
2、部署在同一个服务器上的两个Web应用程序所使用的Java类库可以相互共享。这个需求也很常见,比如相同的Spring类库10个应用程序在用不可能分别存放在各个应用程序的隔离目录中
3、支持热替换,我们知道JSP文件最终要编译成.class文件才能由虚拟机执行,但JSP文件由于其纯文本存储特性,运行时修改的概率远远大于第三方类库或自身.class文件,而且JSP这种网页应用也把修改后无须重启作为一个很大的优势看待
由于存在上述问题,因此Java提供给用户使用的ClassLoader就无法满足需求了。Tomcat服务器就有自己的ClassLoader架构,当然,还是以双亲委派模型为基础的:

来源:https://www.cnblogs.com/xrq730/p/4847337.html

如何自定义类加载器
自定义类加载器的方法很简单,继承ClassLoader类。如果不想破坏双亲委派模型,只需要重写findClass方法即可;如果想破坏双亲委派模型,那么重写整个loadClass方法。当然,一般我们都不想破坏双亲委派模型,具体为什么上文中已经讲了。

本文就讲到这里,本文已收录于Java并发编程札记专栏

本文内容摘录或总结自《深入理解Java虚拟机 JVM高级特性与最佳实践》。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值