一.为什么会有类加载

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. body里面的onload和window.onload,window.load的区别

    区别:body里面的onload是在“页面加载完成后执行的动作”window里面的onload是在“页面加载时执行的动作” window.load这个应该只是表明事件方法,但并未执行,比如click表 ...

  2. 聊天机器人開發好消息!!DIALOGFLOW與微信的天作之合!!

    虽然DIALOGFLOW暂未能够与微信(WECHAT)或企业微信(ENTERPRISE WECHAT)进行任何技制上的连接INTERGRATION),确实限制了我们这些聊天机器人开发者的创作空间,因为 ...

  3. Multi-Projector Based Display Code ---- ImageViewer

    Overview Image viewer is a typical application for large display. It makes use of the high-resolutio ...

  4. 2018年年度总结 & 2019年计划

      2018关键词 「探索」 引用以前作文最爱写的开头,时间如白驹过隙,回想上次写17年年度总结,仿佛也就过了几日光景.   首先回顾一下17年定下的目标, 18年我将关键字设为探索,目的有两个,一是 ...

  5. Spark入门到精通--(第十节)环境搭建(ZooKeeper和kafka搭建)

    上一节搭建完了Hive,这一节我们来搭建ZooKeeper,主要是后面的kafka需要运行在上面. ZooKeeper下载和安装 下载ZooKeeper 3.4.5软件包,可以在百度网盘进行下载.链接 ...

  6. linux内核态和用户态的信号量

    在Linux的内核态和用户态都有信号量,使用也不同,简单记录一下. 1> 内核信号量,由内核控制路径使用.内核信号量是struct semaphore类型的对象,它在中定义struct sema ...

  7. 爬虫下载校花网美女信息-lxml

    # coding=utf-8 # !/usr/bin/env python ''' author: dangxusheng desc : 下载校花网上的个人信息:名字-学校-图片地址-点赞数 date ...

  8. linux----------VMware如何链接局域网其它电脑上的虚拟机,选择桥接模式即可。仔细阅读虚拟机的三个连接方式

    VMWare提供了三种工作模式,它们是bridged(桥接模式).NAT(网络地址转换模式)和host-only(主机模式).要想在网络管理和维护中合理应用它们,你就应该先了解一下这三种工作模式.   ...

  9. JSON 是个什么??!!!

    json就是字符串! json就是字符串! json就是字符串! 重要的事情说三遍!json本质就是字符串,经过序列化的字符串.json的出现只是方便传输.你可以将所有的数据类型用序列化函数序列化js ...

  10. Unity如何退出游戏

    使用 Application.Quit(),但在 editor 模式下使用 Application.Quit()是没用的,要用 EditorApplication.isPlaying = false. ...