类加载机制你们应该已经很是熟悉了,采起双亲委派机制,当加载一个类时,首先将加载任务委托给父类加载器,依次递归,若是父类加载器能够完成加载任务,就成功返回;若是父类没法加载,才由本身加载。 java
双亲委派机制的做用:防止内存中出现多份相同的字节码。mysql
其余规则:
1.隐式加载:在当前类中全部new的对象,若是没有被加载,则使用当前类的类加载器加载 若是类A中引用了类B,Java虚拟机将使用加载类A的类加载器去加载类B
2.不一样类加载器加载的类是不一样的,经过类加载器+类全路径来惟一标识一个类web
JVM预约义的三种类加载器:
1.Bootstrap ClassLoader:启动类加载器,它负责将JAVA_HOME/lib下面的类库加载到内存中,如rt.jar;启动类加载器是由C++写的二进制代码,不是java类,在JVM启动的时候Bootstrap就已经启动。
2.Extension ClassLoader:标准扩展类加载器,它负责加载JAVA_HOME/lib/ext或由系统变量java.ext.dir指定位置中的类库加载到内存中。
3.APP ClassLoader:系统类加载器(System ClassLoader),它负责将类路径CLASSPATH中的类库加载到内存。sql
加载顺序图以下:数据库
图中的BootStrapClassLoader、ExtClassLoader、APPClassLoader不是真正的继承关系,只是逻辑上的上下级类加载器;tomcat
实际上的类关系以下图:服务器
能够看到ExtClassLoader和APPCLassLoader都继承自URLClassLoader,也就证明了两者并不是真正的继承关系;oracle
经过上图能够看到最顶层的类是抽象类ClassLoader:是全部类加载器的基类(除了启动类加载器),定义了类加载最核心的操做;
SecureClassLoader:添加了关联类源码、关联系统权限支持
URLClassLoader:支持从jar文件和文件夹中获取class
ExtClassLoader:扩展类加载器Extension ClassLoader
APPClassLoader:系统类加载器,也称为System ClassLoaderapp
ClassLoader:webapp
父子类加载器是经过ClassLoader一个parent属性来标识,APPClassLoader的父加载器是ExtClassLoader,ExtClassLoader的父加载器是null。
ClassLoader提供了两个构造器,一个是没有参数的,一个是有参数的;以下图:
没有参数的构造器默认将系统类加载器做为parent加载器;
有参数的构造器将参数指定的加载器做为父类加载器;
Launcher:
ExtClassLoader和AppClassLoader都是Launcher的子类,在ClassLoader初始化或者直接经过ClassLoader的getSystemClassLoader()获取的时候会调用initSystemClassLoader(),从而调用sun.misc.Launcher.getLauncher(),将系统类加载器赋值给ClassLoader的scl变量;
咱们看下Launcher类初始化的时候都作了什么工做,如图:
主要是三部工做:
1.建立ExtClassLoader
2.建立AppClassLoader
3.将线程系统类加载器设置为线程上下文类加载器,什么是上线文类加载器?
线程上下文类加载器:
java提供了为不少服务商提供了接口,简称SPI(Service Provider Interface),具体的实现由各厂商提供,例如mysql驱动,oracle驱动等。例如:mysql驱动加载接口类在rt.jar中,由启动类加载器加载,具体实现类在mysql驱动包中,驱动包通常放到咱们本身的程序路径lib下,应该由系统类加载器加载;可是在使用以下代码进行数据库链接使用操做的时候,就会出如今rt.jar中要加载驱动包里代码的状况(类加载器是启动类加载器),由隐式加载规则可知,驱动包也要使用启动类加载器加载,由类加载机制可知,是没法经过启动类加载器来加载的;那这种状况怎么办呢,就要经过线程上下文类加载器来解决。
上面描述的状况以下:使用jdbc进行数据库操做以下
1.Class.forName("com.mysql.jdbc.Driver");// 加载mysql驱动 2.Connection conn = DriverManager.getConnection(url);//建立链接 3.Statement stmt = conn.createStatement();//获得statement对象 4.操做数据库 关闭链接。。
第一步在实例化Driver时,会调用DriverManager的registerDriver()方法收集divers,将驱动类注册到DriverManager容器中,DriverManage的drivers容器:
注册的代码:
Class.forName("com.mysql.jdbc.Driver")至关于:
ClassLoader loader = Thread.currentThread().getContextClassLoader();
Class driversClass = loader.loadClass("com.mysql.jdbc.Driver");
driversClass.newInstance();
因而可知com.mysql.jdbc.Driver是由咱们本身应用类加载器AppClassLoader进行加载;
第二步经过DriverManager.getConnection(url),会循环获取drivers中的driver,调用具体driver实现里的cnnect()方法,进行链接
caller.getClassLoader()是启动类加载器为null,所以callerCL为系统类加载器
getConnection经过isDriverAllowed方法校验类是否有权限被加载
经过AppClassLoader来加载Driver看是否和已注册的Driver是同一个类,若是是则调用driver的connect方法
在java6之后,引入了service provider概念,在/META-INF/services/java.sql.Driver文件中配置须要加载的驱动类,
在DriverManager初始化的时候会调用loadInitialDrivers方法,
会使用AppClassLoader进行加载,因此在本身程序中能够不用Class.forName显示调用。
上面包类结构以下图:
tomcat类加载
咱们运行tomcat的多个实例,不想安装tomcat软件副本,咱们能够配置多个工做目录,每一个运行实例独占一个工做目录,可是共享一个安装目录。
变量解释:
CATALINA_HOME:tomcat安装目录,多个工做目录可共享安装目录
CATALINA_BASE:tomcat工做目录,tomcat每一个运行实例须要使用本身的conf、logs、temp、webapps、work、shared目录,CATALINA_BASE就是指向这个目录
以下图:两个应用公用CATALINA_HOME,CATALINA_BASE指向各自工做目录
首先看下tomcat在启动时类初始化类加载器过程
首先是建立commonClassLoader,commonClassLoader加载的是配置文件catalina.properties中配置的
common.loader=${catalina.home}/lib,${catalina.home}/lib/*.jar
server.loader=
shared.loader=
例如在咱们的服务器上路径是:
/opt/soft/tomcat/lib、/opt/soft/tomcat/lib/*.jar
由于server.loader和shared.loader未配置具体加载目录信息,catalinaLoader和sharedLoader默认为commonLoader(在tomcat5之后catalinaLoader和sharedLoader默认不启用)
tomcat一共定义了两种类加载器
StandardClassLoader:实例化commonloader、catalinaLoader、sharedLoader,不提供热部署功能,遵循双亲委派机制
WebappClassLoader:和context级容器相关联,加载web程序,支持其加载路径下资源改变后从新加载,不遵循双亲委派机制。
其类继承关系以下:
除此以外还有两个类:WebappLoader和VirtualWebappLoader,该两个类不是类加载器,只是对WebappClassLoader作了封装,对热部署、生命周期控制等功能作了一些控制;
VirtualWebappLoader是WebappLoader的子类,主要是和conf/context.xml这个文件相关联,主要功能是加载context.xml配置文件中设置的一些java类库,因为WebappClassLoader只能加载WEB-INF/class和WEB-INF/lib下的类库。而想扩展一下加载路径又不想添加到WEB-INF/lib中的时候,能够配置在context.xml文件中。
本身实现类记载器只要实现findclass便可,这里为了实现特殊目的而override了loadClass();WebappClassLoader重写了loadClass方法,先本身加载,若是加载不了再进行其余操做。
因此在Tomcat 6中默认状况下,不是彻底按照先Tomcat的lib再Web应用的lib这种顺序去加载类。
Jar包的加载顺序是:
1)JRE中的Java基础包
2)Web应用WEB-INF/lib下的包
3)Tomcat/lib下的包
若是想要在Web应用间共享一些Jar包,则不只须要将公共包放在Tomcat的lib下,还要删掉Web应用lib中的包,不然Tomcat启动时仍是会优先加载Web应用lib下的包的。
若是想要本身指定一个Tomcatlib和Web应用lib以外的ClassPath,除了修改Tomcat启动脚本外,能够为不一样Web应用的Context指定一个VirtualWebappLoader,但源码注释中写到不推荐在生产环境中使用。