一.为什么会有类加载

1.在类加载阶段,虚拟机需要完成以下3件事情

1)通过一个全限类定名来获取此类的二进制字节流

2) 将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构

3)在内存中生成一个代表这个类的java.lang.Class 对象,作为方法区这个类的各种数据结构访问入口

2.虚拟机将类加载过程分为三个步骤:装载(Load),链接(Link)和初始化(Initialize)链接又分为三个步骤,如下图所示:

2)链接:

  验证:确保被加载类的正确性;(验证不过会抛出 java.lang.VerifyError或其子类异常)

  准备:为类的静态变量分配内存,并将其初始化为默认值;

  解析:把类中的符号引用转换为直接引用;

3)初始化:为类的静态变量赋予正确的初始值;

那为什么我要有验证这一步骤呢?首先如果由编译器生成的class文件,它肯定是符合JVM字节码格式的,但是万一有高手自己写一个class文件,让JVM加载并运行,用于恶意用途,就不妙了,因此这个class文件要先过验证这一关,不符合的话不会让它继续执行的,也是为了安全考虑吧。

准备阶段和初始化阶段看似有点牟盾,其实是不牟盾的,如果类中有语句:private static int a = 10,它的执行过程是这样的,首先字节码文件被加载到内存后,先进行链接的验证这一步骤,验证通过后准备阶段,给a分配内存,因为变量a是static的,所以此时a等于int类型的默认初始值0,即a=0,然后到解析(后面在说),到初始化这一步骤时,才把a的真正的值10赋给a,此时a=10。

在上述的整个过程中 链接(Link)和初始化(Initialize) 都是由虚拟机完成的,而装载则虚拟机则允许开发者自定义完成。

二.Java虚拟机类加载

Java虚拟机提供了三个类加载器

  1.启动类加载器:Bootstrap ClassLoader,跟上面相同。它负责加载存放在JAVA_HOME\jre\lib(JAVA_HOME代表JDK的安装目)下,或被-Xbootclasspath参数指定的路径中的,并且能被虚拟机识别的类库(如rt.jar,所有的java.*开头的类均被Bootstrap ClassLoader加载)。启动类加载器是无法被Java程序直接引用的。

  2.扩展类加载器: Extension ClassLoader,该加载器由sun.misc.Launcher$ExtClassLoader实现,它负责加载JAVA_HOME\jre\lib\ext目录中,或者由java.ext.dirs系统变量指定的路径中的所有类库(如javax.*开头的类),开发者可以直接使用扩展类加载器。

  3.系统类加载器: Application ClassLoader,该类加载器由sun.misc.Launcher$AppClassLoader来实现,它负责加载用户类路径ClassPath  (System.getProperty("java.class.path"))所指定路径下的类,开发者可以直接使用该类加载器,如果应用程序中没有自定义过自己的类加载器,一般情况下这个就是程序中默认的类加载器。

三.自定义类加载器

除了上面三个类加载器外,开发者还可以自定义类加载器,自定义类加载器需要实现 ClassLoader这个抽象类,可以选择重载 loadClass 或者 findClass方法。

一个自定义类加载器的例子

 package com.dianping.loader;

 import java.io.InputStream;

 public class CustomerLoader extends ClassLoader {

     public CustomerLoader() {

     }

     public CustomerLoader(ClassLoader parent) {
super(parent);
} @Override
public Class<?> loadClass(String name) throws ClassNotFoundException {
try { Class c = findLoadedClass(name);
if (c != null) return c; if (getParent() != null) {
try {
c = getParent().loadClass(name);
} catch (ClassNotFoundException e) {
}
} if (c == null) {
String fileName = name.substring(name.lastIndexOf(".") + 1) + ".class";
InputStream in = getClass().getResourceAsStream(fileName);
if (in == null) {
return super.loadClass(name);
}
byte[] bytes = new byte[in.available()];
in.read(bytes);
c = defineClass(name, bytes, 0, bytes.length);
}
return c;
} catch (Exception e) {
throw new ClassNotFoundException(name);
}
} }

四、类加载的双亲委任模型

  类加载器虽然只用于实现类的加载动作,但它在Java程序中起到的作用却远远不限于类加载阶段。对于任意一个类,都需要由加载它的类加载器和这个类本身一同确立其在Java虚拟机中的唯一性,每一个类加载器,都拥有一个独立的类名称空间。 这句话可以表达得更通俗一些:比较两个类是否“相等”,只有在这两个类是由同一个类加载器加载的前提下才有意义,否则,即使这两个类来源于同一个Class文件,被同一个虚拟机加载,只要加载它们的类加载器不同,那这两个类就必定不相等。
这里所指的“相等”,包括代表类的Class对象的equals()方法、 isAssignableFrom()方法、 isInstance()方法的返回结果,也包括使用instanceof关键字做对象所属关系判定等情况。
JVM的类加载的实现方式称为双亲委任模型,其流程是当收到一个类加载请求时,首先会交给父加载器完成,如果父加载器反馈自己无法加载时,子加载器才尝试自己完成加载,每一层次的类加载器都是如此。

  双亲委派模型要求除了顶层的启动类加载器外,其余的类加载器都应当有自己的父类加载器。 这里类加载器之间的父子关系一般不会以继承(Inheritance)的关系来实现,而是都使用组合(Composition)关系来复用父加载器的代码。

  双亲委任可以解决了类加载过程中的安全性问题,如果定义了一个Java基础类库中的一个类,使用双亲委任模型保证了Java基础类的加载由上层类加载器优先加载。这样可以确保即使用户编写了一个java.lang.Object的类也不会影响虚拟机加载rt.jar下的java.lang.Object的类。

当然双亲模型只是一个规范,tomcat并不是完全按照双亲模型规范的。

五、Tomcat类加载

在tomcat中类的加载稍有不同,tomcat并没有完全遵守双亲模型

1.在tomcat中 会初始化三个类加载器  commonLoader, catalinaLoader, sharedLoader  这三个类加载器都是继承 URLClassLoader这个类

加载类的路径是 ${tomcat_home}/conf/catalina.properties  文件配置的

catalinaLoader 对应的配置是 server.loader ,sharedLoader对应的是 shared.loader

默认的情况server.loader 和 shared.loader 都是没有配置,所以catalinaLoader,和 sharedLoader 其实都是 commonLoader ,他们父类加载器都是sun.misc.Launcher$AppClassLoader系统类加载器

 #
#
# List of comma-separated paths defining the contents of the "common"
# classloader. Prefixes should be used to define what is the repository type.
# Path may be relative to the CATALINA_HOME or CATALINA_BASE path or absolute.
# If left as blank,the JVM system loader will be used as Catalina's "common"
# loader.
# Examples:
# "foo": Add this folder as a class repository
# "foo/*.jar": Add all the JARs of the specified folder as class
# repositories
# "foo/bar.jar": Add bar.jar as a class repository
common.loader=${catalina.base}/lib,${catalina.base}/lib/*.jar,${catalina.home}/lib,${catalina.home}/lib/*.jar #
# List of comma-separated paths defining the contents of the "server"
# classloader. Prefixes should be used to define what is the repository type.
# Path may be relative to the CATALINA_HOME or CATALINA_BASE path or absolute.
# If left as blank, the "common" loader will be used as Catalina's "server"
# loader.
# Examples:
# "foo": Add this folder as a class repository
# "foo/*.jar": Add all the JARs of the specified folder as class
# repositories
# "foo/bar.jar": Add bar.jar as a class repository
server.loader= #
# List of comma-separated paths defining the contents of the "shared"
# classloader. Prefixes should be used to define what is the repository type.
# Path may be relative to the CATALINA_BASE path or absolute. If left as blank,
# the "common" loader will be used as Catalina's "shared" loader.
# Examples:
# "foo": Add this folder as a class repository
# "foo/*.jar": Add all the JARs of the specified folder as class
# repositories
# "foo/bar.jar": Add bar.jar as a class repository
# Please note that for single jars, e.g. bar.jar, you need the URL form
# starting with file:.
shared.loader=

2.catalinaLoader 主要用于加载Tomcat代码

3.每一个web应用会对应一个WebappClassLoader,每个应用的WebappClassLoader是不一样的,这样可以确保应用间隔离。 WebappClassLoader的父类加载器是sharedLoader,因为默认情况sharedLoader是commonLoader,所以默认情况WebappClassLoader的父类加载器是commonLoader。

这个里说Tomcat没有完全准守双亲模型的原因是  在每一个应用中,WebappClassLoader加载类的时候会有以下步骤

1. 看类是不是可以被系统类加载器加载,如果可以在返回

2. 如果这个类不可以被系统类加载加载,这交给WebappClassLoader 加载,WebappClassLoader会在WEB-INF/classes中查找

3. 在WEB-INF/classes中没有找到,则会在WEB-INF/lib中查找

4.如果在WEB-INF/lib中也没有找到则会交给父类加载加载,默认情况父类加载器是commonLoader。

5. 如果以上没有找到,则抛出异常。

WebappClassLoader会对这个类的结果进行缓存,即找到了则缓存结果,找不到也会缓存找不到,所以以上行为其实是web应用第一次加载类的时候才会发生。

六、总结当tomcat 的WEB应用需要到某个类时,则会按照下面的顺序进行类加载

  1. 使用启动类加载器:Bootstrap ClassLoader      加载 JAVA_HOME\jre\lib

2. 使用扩展类加载器: Extension ClassLoader     加载JAVA_HOME\jre\lib\ext

  3. 使用系统类加载器: Application ClassLoader    它负责加载用户ClassPath  (System.getProperty("java.class.path") 这个参数所指定的文件夹)所指定的类

  4. 使用应用类加载器 WebappClassLoader          在该应用的WEB-INF/classes中路径中查找

  5. 使用应用类加载器 WebappClassLoader          在WEB-INF/lib中查找

  6. 使用sharedLoader类加载器(默认情况下sharedLoader是commonLoader)    在CATALINA_HOME/lib中加载

参考地址:1. 图解Tomcat类加载机制

2.Java类加载器总结

Tomcat类加载的更多相关文章

  1. java类加载器-Tomcat类加载器

    在上文中,已经介绍了系统类加载器以及类加载器的相关机制,还自定制类加载器的方式.接下来就以tomcat6为例看看tomat是如何使用自定制类加载器的.(本介绍是基于tomcat6.0.41,不同版本可 ...

  2. 图解Tomcat类加载机制

    说到本篇的tomcat类加载机制,不得不说翻译学习tomcat的初衷. 之前实习的时候学习javaMelody的源码,但是它是一个Maven的项目,与我们自己的web项目整合后无法直接断点调试.后来同 ...

  3. Tomcat类加载器机制

    Tomcat为什么需要定制自己的ClassLoader: 1.定制特定的规则:隔离webapp,安全考虑,reload热插拔 2.缓存类 3.事先加载 要说Tomcat的Classloader机制,我 ...

  4. Tomcat类加载器

    1JVM类加载机制   JVM的ClassLoader通过Parent属性定义父子关系,可以形成树状结构.其中引导类.扩展类.系统类三个加载器是JVM内置的. 它们的作用分别是: 1)引导类加载器:使 ...

  5. 深入剖析Tomcat类加载机制

    1JVM类加载机制 JVM的ClassLoader通过Parent属性定义父子关系,可以形成树状结构.其中引导类.扩展类.系统类三个加载器是JVM内置的. 它们的作用分别是: 1)引导类加载器:使用n ...

  6. Tomcat系列(7)——Tomcat类加载机制

    1. 核心部分 1. 类加载器: 通过一个类的全限定名来获取描述此类的二进制字节流. 对于任意一个类,都需要由加载他的类加载器和这个类本身一同确立其在Java虚拟机中的唯一性,每一个类加载器,都拥有一 ...

  7. 图解Tomcat类加载机制(阿里面试题)

    Tomcat的类加载机制是违反了双亲委托原则的,对于一些未加载的非基础类(Object,String等),各个web应用自己的类加载器(WebAppClassLoader)会优先加载,加载不到时再交给 ...

  8. 《转载》图解Tomcat类加载机制

    本文转载自http://www.cnblogs.com/xing901022/p/4574961.html 说到本篇的tomcat类加载机制,不得不说翻译学习tomcat的初衷. 之前实习的时候学习j ...

  9. Tomcat类加载器破坏双亲委派

    转载:https://blog.csdn.net/qq_38182963/article/details/78660779 http://www.cnblogs.com/aspirant/p/8991 ...

随机推荐

  1. 小甲鱼零基础python课后题 P22 021函数:lambda表达式

    0.请使用lambda表达式将下边函数转变为匿名函数 def fun_A(x,y=3): return x*y 答: lambda x,y=3:x*y 1.请将下边的匿名函数转变为普通的屌丝函数 la ...

  2. .net不同集合类型及使用场合

    1.Dictionary-相当于字典[可以通过过索引(hash值)速添加.删除.查找]:如果需要非常快地添加.删除和查找项目,而且不关心集合中项目的顺序,那么首先应该考虑使用 System.Colle ...

  3. Java中的常用集合类型总结

    1.可重复列表(List) LinkedList和ArrayList的区别:http://www.importnew.com/6629.html ArrayList vs. LinkedList vs ...

  4. centos下设置nodejs开机启动

    node环境的安装便不再赘述了,网上有很多教程,也非常简单. 上一篇博客介绍了用nginx代理nodejs.这一篇是使用pm2实现nodejs的自动重启. 什么是pm2? 如官网介绍的,pm2是nod ...

  5. webToImage (网页转图片)模块试用分享

    模块介绍: 本模块封装了把 webview 转换成图片的功能.调用本模块的transImage接口,可把当前 webview显示的内容转换成一张图片.注意,本模块只能把当前的webview页面转换为图 ...

  6. VUE-003-前端表格数据展示时,设置单元格(el-table-column)保留空格和换行

    在使用 el-table 展示数据时,单元格中的数据有可能存在空格和换行符,若不进行设置,浏览器默认会取消空格和换行符,如下所示: 解决方法: 将单元格的样式 “white-space” 属性设置为“ ...

  7. match 和 lastIndex 字符串检测差异

    match .replace .search 这写不能识别特殊字符 indexOf .indexof 能识别特殊字符 str.lastIndexOf('a') > -1 // 通过lastInd ...

  8. ES6的Proxy和Reflect

    Proxy 有一个原始的数据对象,通过代理出来一个新的对象,用户操作的是这个新的对象 { let obj ={ time:'2018-01-01', name:'lx' , _r:123 } let ...

  9. 记录请求的耗时(拦截器、过滤器、aspect)

    文章前言 记录控制器请求的耗时处理通常有三种实现方式,分别是:过滤器.拦截器.aspect:下文将逐一实现. 1.Filter 过滤器 1.1.方法说明 需要实现 Filter 类,主要涉及三个方法: ...

  10. python基础之 序列化,os,sys,random,hashlib

    1.序列化 定义: JSON(JavaScript Object Notation) 是一种轻量级的数据交换格式.简单地说,JSON 可以将 JavaScript 对象中表示的一组数据转换为字符串,然 ...