从代码热替换谈谈类加载机制

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/wueryan/article/details/89647957

知识点

  • 谈谈Tomcat的代码热替换与模块的热部署
    • 各自使用场景
    • 实现原理
    • 类隔离
  • 了解类加载机制与双亲委派机制
    • 概念
    • 类加载器
    • 如何破坏双亲委派机制
    • 其他应用场景

目标

我希望:
(1)学习到代码热替换/模块热部署的实现原理
(2)了解Java的类加载机制、双亲委派机制
(3)破坏双亲委派机制的场景

正文

玩过Tomcat的朋友都应该了解,在进入Web工程开发的过程中,我们常常能不用重启服务器的前提下,能感知到我们对Jsp文件或者类文件做的修改,这有赖于Tomcat本身的热部署与热加载机制了。

那么这里所说的热部署和热加载究竟是什么呢?

热部署(Hot Deployment)

热部署往往是针对模块级别的,整个项目是重新部署,这时候自然而然的就会重新加载刚更新上来的war文件,释放内存。往往热部署的时间会更加长一点。

热加载(HotSwap)

热加载则是服务器会监听class文件的改变,是不释放内存和session,是属于一种局部加载。也有另一种称法叫做代码的热替换(HotSwap)

概念性的东西不再赘述,但是有必要澄清一点,个人的理解,热部署更多倾向于模块级别的,而热加载往往针对于代码级别的,是属于局部加载,从而实现无停机地平滑部署。
但是无论是热部署还是热加载,都面临着性能以及稳定性的缺陷,当然这一块并不在本篇文章的讨论范围内。

具体实现

Tomcat本身是既支持热部署,同时也是支持热加载的。

关于Tomcat热部署的实现原理,主要分为两步:
(1)监测类文件的时间戳变化
(2)如果时间戳变了,则直接调用容器的reload方法,将类重新载入。就是重新启动整个容器。
具体核心代码见下:

在这里插入图片描述
在这里插入图片描述

围绕class文件和jsp文件被修改,Tomcat的处理并不相同。
对于class文件被更改后,在Tomcat的Context容器会有专门的线程监控app下面的类文件,对于这里的类文件被更改,Tomcat调用的时Context.reload(),清理掉相关引用和资源,之后创建一个WebClassLoader实例,重新加载app下面需要的class文件。

但是对于jsp文件则又有不同,JspServletWrapper会去比较文件是否修改,如果修改,对应的jspservletWrapper会清理掉相关引用。之后重新创建JasperLoader实例,重新去加载修改过后的jsp文件。

其实在这里无论是class文件的加载还是jsp文件的加载,都需要了解到Java的类加载机制,这也是热部署以及热加载的核心原理。

关于类加载与双亲委派机制

什么是类加载

我们通常把描述类的数据(或者称之为信息)记录在一组二进制的字节流(比如Class文件或者其他),那么当我们从这些源加载进内存,同时对其进行校验、解析和初始化,这个过程,我们就叫做类加载机制。

那么执行类加载的动作的,我们通常称之为类加载器。

类加载器
通过一个类的全限定名来获取描述此类的二进制字节流,将这个动作放在Java虚拟机去实现,以便让应用程序自己决定如何去获取所需要的类。

类加载器的分类在jdk1.7分为三类,主要为Bootstrap ClassLoader,ExtClassLoader,AppClassLoader。

BootstrapClassLoader 加载/jre/lib路径下,以及Xbootclasspath制定的路径下/jre/classes下的类

ExtClassLoader:拓展类类加载器,它用来加载<JAVA_HOME>/jre/lib/ext路径以及java.ext.dirs系统变量指定的类路径下的类。

AppClassLoader:应用程序类类加载器,它主要加载应用程序ClassPath下的类(包含jar包中的类)。它是java应用程序默认的类加载器。

类加载器的划分维度并非关键,这里需要知道的是为了避免重复加载、或者多个类加载器各自加载自己的类导致类加载混乱的情况,Java提供了一套模型:双亲委派模型。
在这里插入图片描述

这套模型并非强制约束的,模型分为以下几点:
(1)除顶层类加载器(BootstrapClassLoader)外,其他均要有自己的父类加载器。
(2)当接收到类加载的请求时,理应询问自己的父类加载器,直到顶层类加载器,只有他们不存在此类,方可于自己进行加载。

Java本身为保证稳定提出了双亲委派机制的模型,但是双亲委派机制并非不可破坏,毕竟它只是一个建议的设计,而非强制。

类加载机制以及双亲委派机制其实并不难以理解,但是实际上Tomcat的一些特性则是通过破坏双亲委派机制来进行实现。

Tomcat的类加载

对于Tomcat来说,需要支持一个Tomcat内部署多个应用,这就要求做到同名class类的隔离,同时还要保证不过多的占用内存。

那么针对这个问题,Tomcat本身是如何做类加载的模型设计的呢?

     Bootstrap
          |
       System
          |
       Common
       /     \
  Webapp1   Webapp2 ...

除了Bootstrap和System这两类Loader,此外Tomcat本身加入了Common和Webapp两类Loader。

Web类加载器与Common类加载器

对于Webapp类加载器来说,有两点是和双亲委派机制不一致的,双亲委派机制模型是以Parent优先,但是如果以这种方式来进行设计,则无法处理Tomcat部署多个应用的问题,因此对于Webapp类加载器而言,它必然是以子优先的,同时对于每一个应用来说,每一个Webapp类加载器也应该是独立的,不互相干扰的,所管理的应是该应用下自己的class文件以及其他资源文件。

该类加载器会加载Web应用的WEB-INF/classes内的class和资源文件,以及WEB-INF/lib下的所有jar文件。

这样一来,就很好地解决了不同应用下同名类文件的加载冲突问题。

但是随之而来的另一个问题,则是资源利用率的问题,如果每一个应用都只顾自己,对于所有应用都共同应用的jar包下的类文件都自己加载进来一份,那么可以想象内存空间将会有很大的浪费。
针对这个问题,Tomcat引入了Common类加载器。

对于Tomcat源码的解析,不在本篇进行赘述,可以参考Tomcat类加载器以及应用间class隔离与共享
个人感觉这一篇文章讲的还是十分清晰的。

参考文章

class卸载、热替换和Tomcat的热部署的分析
知乎·类加载器
Tomcat类加载器以及应用间class隔离与共享
Tomcat 类加载器之为何违背双亲委派模型

猜你喜欢

转载自blog.csdn.net/wueryan/article/details/89647957
今日推荐