虚拟机设计团队把加载动作放到JVM外部实现,以便让应用程序决定如何获取所需的类,JVM提供了4种类加载器:
1.类加载器种类
启动类加载器(Bootstrap ClassLoader):负责加载 JAVA_HOME\lib 目录中的,或通过-Xbootclasspath参数指定路径中的,且被虚拟机认可(按文件名识别,如rt.jar)的类。一般是jdk/jre/lib
扩展类加载器(Extension ClassLoader):负责加载 JAVA_HOME\lib\ext 目录中的,或通过java.ext.dirs系统变量指定路径中的类库。
应用程序类加载器(Application ClassLoader):负责加载用户路径(classpath)上的类库。
自定义类加载器,通过继承Application ClassLoader实现,一般是加载我们的自定义类
2.双亲委派模型
类加载器 Java 类如同其它的 Java 类一样,也是要由类加载器来加载的;除了启动类加载器,每个类都有其父类加载器(父子关系由组合(不是继承)来实现);
所谓双亲委派是指每次收到类加载请求时,先将请求委派给父类加载器完成(所有加载请求最终会委派到顶层的Bootstrap ClassLoader加载器中),如果父类加载器无法完成这个加载(该加载器的搜索范围中没有找到对应的类),子类尝试自己加载。
![](https://img.haomeiwen.com/i10059245/822bbbe34a5deaf4.png)
当一个类加载器收到类加载任务,会先交给其父类加载器去完成,因此最终加载任务都会传递到顶层的启动类加载器,只有当父类加载器无法完成加载任务时,才会尝试执行加载任务。
采用双亲委派的一个好处是比如加载位于rt.jar包中的类java.lang.Object,不管是哪个加载器加载这个类,最终都是委托给顶层的启动类加载器进行加载,这样就保证了使用不同的类加载器最终得到的都是同样一个Object对象。
在有些情境中可能会出现要我们自己来实现一个类加载器的需求,由于这里涉及的内容比较广泛,我想以后单独写一篇文章来讲述,不过这里我们还是稍微来看一下。我们直接看一下jdk中的ClassLoader的源码实现:
![](https://img.haomeiwen.com/i10059245/3a9f36a7a97b4311.png)
首先通过Class c = findLoadedClass(name);判断一个类是否已经被加载过。
如果没有被加载过执行if (c == null)中的程序,遵循双亲委派的模型,首先会通过递归从父加载器开始找,直到父类加载器是Bootstrap ClassLoader为止。
最后根据resolve的值,判断这个class是否需要解析。
而上面的findClass()的实现如下,直接抛出一个异常,并且方法是protected,很明显这是留给我们开发者自己去实现的,这里我们以后我们单独写一篇文章来讲一下如何重写findClass方法来实现我们自己的类加载器。
![](https://img.haomeiwen.com/i10059245/4701e918b178baf9.png)
双亲委派好处:
避免同一个类被多次加载;
每个加载器只能加载自己范围内的类;
3.自定义类加载器
要创建用户自己的类加载器,只需要继承java.lang.ClassLoader类,然后覆盖它的findClass(String name)方法即可,即指明如何获取类的字节码流。
如果要符合双亲委派规范,则重写findClass方法(用户自定义类加载逻辑);要破坏的话,重写loadClass方法(双亲委派的具体逻辑实现)。
![](https://img.haomeiwen.com/i10059245/fdc886656041e3cf.png)
原因:
比较典型的自定义classloader使用情况就是给类加密。java编译的代码可以轻易的被反编译,有些企业会给自己的类做特殊的加密,防止反编译,类加密后就不能再用java的classloader去加载类了,这时就需要自定义classloader,再加载类的时候先解密类,然后再加载。
4.java热部署
热部署与热加载
在应用运行的时候升级软件,无需重新启动的方式有两种,热部署和热加载。
它们之间的区别是:
(1)部署方式:
热加载:监听class,config等文件,保持局部与整体的统一性,一般会重新加载被修改的类或者配置,不会清除缓存。
热部署:监听的是打包完成的文件,如war,ear等,如果发生变化,就会重新部署一次,先释放内存,再加载新的包,会清空session。项目打包的时候用的多
(2)实现原理
热部署直接重新加载整个应用,这种方式会释放内存,比热加载更加干净,但是它比热加载更加的浪费时间。
热加载在运行时重新加载class,主要使用的是类加载机制,在实现方式上就是在容器启动的时候起一条后台线程,定时的检测类文件的时间戳变化,如果类的时间戳发生变化,则将类重新载入。
(3)tomcat配置
热加载:在server.xml -> context 属性中 设置 reloadable="true"
<Context docBase="xxx" path="/xxx" reloadable="true"/>
热部署:在server.xml -> context 属性中 设置 autoDeploy="true"
<Context docBase="xxx" path="/xxx" autoDeploy="true"/>
(3)使用场景
热部署更多的是在生产环境中使用,就是适用于频繁的部署并且启动耗时长的应用、无法停止服务的应用等。
热加载则更多的是在开发环境中使用,用于开发,debug中,可以大大的提高开发效率。
网友评论