一.为什么会有类加载

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. ASP.NET Core 2.2 迁移至 3.0 备忘录

    将 ASP.NET Core 2.2 迁移至 ASP.NET Core 3.0 需要注意的地方记录在这篇随笔中. TargetFramework 改为 netcoreapp3.0 <Target ...

  2. Gym 101606F - Flipping Coins - [概率DP]

    题目链接:https://codeforc.es/gym/101606/problem/F 题解: 假设 $f[i][j]$ 表示抛 $i$ 次硬币,有 $j$ 个硬币正面朝上的概率. 所以只有两种挑 ...

  3. mysql中in的用法

    今天在工作中遇到一个查询问题,两张表的联合查询,本来我的想法是先查询一张表,然后遍历查到的所有表,拿到id,去另外一张表查询,这样做也能解决问题,但是这样的话,就用不了框架里的分页了,自己写分页比较麻 ...

  4. 阿里云入坑指南&&nodejs 安装配置

    买了阿里云1G1核1M的机器(800元,3年) 登录阿里云-实例-选择实例所在地区-重置密码 用公网IP ssh连接 #升级CentOS yum -y update #安装or更新组件 yum -y ...

  5. NABCD---生活日历

    本软件的主体为日历.如今人们的生活趋于现代化,大家主要通过手机来查看日期,但我们每一天都有想要做或未完成的事情,也存在对自己有特殊意义的日子.每天人们都会有或大或小的支出与收入,想要系统的记录这些支出 ...

  6. 理解 Memory barrier(内存屏障)无锁环形队列

    原文:https://www.cnblogs.com/my_life/articles/5220172.html Memory barrier 简介 程序在运行时内存实际的访问顺序和程序代码编写的访问 ...

  7. java -cp通配符

    JDK6支持java -cp后面跟通配符'*',试了一下发现还是需要注意: 错误方式(Wrong way): java  -cp /data/apps/lib/*.jar com.chinacache ...

  8. su: authentication failure 解决方法

    在Linux上切换root时,密码正确..但提示:su: authentication failure ->sudo passwd ->Password:你当前的密码 ->Enter ...

  9. springcloud第三步:发布服务消费者

    服务消费者 创建项目sercice-order Maven依赖 <parent> <groupId>org.springframework.boot</groupId&g ...

  10. db2一、查询

    1.查询单条数据( fetch 放在最后) select * from x where 1=1 order by id fetch first 1 rows only