title: Java类加载器详解
date: 2015-10-20 18:16:52
tags: JVM
---

## JVM三种类型的类加载器
- 我们首先看一下JVM预定义的三种类型类加载器,当一个 JVM 启动的时候,Java 缺省开始使用如下三种类型类装入器:
- **启动类加载器(Bootstrap Class Loader)**:引导类装入器是用本地代码实现的类装入器,它负责将 <Java_Runtime_Home>/lib 下面的类库加载到内存中。由于引导类加载器涉及到虚拟机本地实现细节,开发者无法直接获取到启动类加载器的引用,所以不允许直接通过引用进行操作。
- **标准扩展类加载器(Extensions Class Loader)**:扩展类加载器是由 Sun 的 ExtClassLoader (sun.misc.Launcher$ExtClassLoader) 实现的。它负责将 < Java_Runtime_Home >/lib/ext 或者由系统变量 java.ext.dir 指定位置中的类库加载到内存中。开发者可以直接使用标准扩展类加载器。
- **系统类加载器(System Class Loader )**:系统类加载器是由 Sun 的 AppClassLoader (sun.misc.Launcher$AppClassLoader)实现的。它负责将系统类路径(classpath)中指定的类库加载到内存中。开发者可以直接使用系统类加载器。

## 类加载双亲委派机制介绍和分析
**为什么是双亲委派机制**:java.lang.String类在rt.jar中,在JVM启动时由BootStrap启动类加载器加载;当用户自定义的加载器也需要加载java.lang.String类时,如果自定义的加载器不请求双亲加载(检查双亲是否已经加载),就会有2个java.lang.String类了。所以双亲委派机制解决了这个问题:一个类加载器在接收到类加载请求的时候,首先将其委托给父类加载器,如果父类加载器可以完成类加载任务,就成功返回;如果父类加载器无法完成加载任务时,才去自己加载。

**双亲委派机制很好解决了各个类加载器类加载的统一问题。**
![Alt text](http://7xlamb.com1.z0.glb.clouddn.com/classloaderClassLoader1.gif)
系统类加载器的父类加载器是标准扩展类加载器,标准扩展类加载器的父类加载器是启动类加载器
``` java
public static void main(String[] args) {
try {
System.out.println(ClassLoader.getSystemClassLoader());
System.out.println(ClassLoader.getSystemClassLoader().getParent();
System.out.println(ClassLoader.
getSystemClassLoader().getParent().getParent());
} catch (Exception e) {
e.printStackTrace();
}
}
```
通过ClassLoader.getSystemClassLoader()可以直接获取到系统类加载器,系统类加载器父类是扩展类加载器,扩展类加载器的父类则是启动类加载器,在JVM中的体现是null,因为启动类加载器是用native的c++实现的。以上代码结果如下:
``` java
sun.misc.Launcher$AppClassLoader@197d257
sun.misc.Launcher$ExtClassLoader@7259da
null
```
通过以上的代码输出,我们可以判定系统类加载器的父加载器是扩展类加载器,但是我们试图获取启动类加载器的父类加载器时确得到了null,就是说扩展类加载器本身强制设定父类加载器为null。我们还是借助于代码分析一下:
``` java
protected ClassLoader() {
SecurityManager security = System.getSecurityManager();
if (security != null) {
security.checkCreateClassLoader();
}
//默认将父类加载器设置为系统类加载器,getSystemClassLoader()获取系统类加载器
this.parent = getSystemClassLoader();
initialized = true;
}

protected ClassLoader(ClassLoader parent) {
SecurityManager security = System.getSecurityManager();
if (security != null) {
security.checkCreateClassLoader();
}
//强制设置父类加载器
this.parent = parent;
initialized = true;
}
```

## 线程上下文类加载器(Thread Context Claass Loader,TCCL)
双亲委派机制解决了类重复加的问题,但是不能解决应用开发中遇到的全部类加载问题。双亲委派机制中,上层父类的加载器不可以使用子类加载的对象。而有些时候程序的确需要父类调用子类对象,这时候就需要线程上下文加载器来处理。

> Java 提供了很多服务提供者接口(Service Provider Interface,SPI),允许第三方为这些接口提供实现。 以JNDI为例,它的核心接口是由JRE核心类(rt.jar)实现的,由启动类加载器加载。但是在这核心类JNDI的实现是由第三方厂商实现的,由系统类加载器加载。启动类加载器是无法找到第三方实现类,因为它只加载 Java 的核心库。

> 但这些核心接口的实现类必须能加载由第三方厂商提供的JNDI实现。这种情况下父类请求子类加载器去完成类加载任务(这个类只有子类加载器可见),双亲委派机制就会失效。解决办法就是让核心JNDI类使用线程上下文类加载器,从而有效的打通类加载器层次结构,逆着代理机制的方向使用类加载器。

**线程上下文类加载器(Thread Context ClassLoader) **
线程上下文加载器可以通过java.lang.Thread类的setContextClassLoader()方法进行设置,如果创建线程时还未设置,默认是系统类加载器。

### Thread Context Class Loader(TCCL)
> TCCL is a hack that was added in Java 1.2 to support J2EE. Specifically it was needed to support things like Entity Beans; in a modern world it's used to support technologies like JPA, JAXB, Hibernate and so on.

先来看个TCCL的例子,自定义类加载器myUrlCl(URLClassLoader)
```java
public class MyURLClassLoaderTest{
public static void main(String[] args) throws Exception {
URL[] baseUrls = { URL("file:D:/java_tools/maven2/m2/repository
/com/xxx/common/common-test/1.0-SNAPSHOT/
common-test-1.0-SNAPSHOT.jar")};

String targetClassName = "com.xxx.common.HelloWorld";
// 设置系统类加载器为myUrlCl的父类加载器
ClassLoader systemCl = ClassLoader.getSystemClassLoader();
//
ClassLoader myUrlCl = new URLClassLoader(baseUrls,systemCl);
// 设置TCCL为myUrlCl,
Thread.currentThread().setContextClassLoader(myUrlCl);

Class targetClass = myUrlCl.loadClass(targetClassName);
Object targetObj = targetClass.newInstance();
Object res = targetClass.getMethod("test").invoke(targetObj);
System.out.println(res);
}
}
```
其中com/xxx/common/common-test/1.0-SNAPSHOT 这个jar包只有两个类,分别是:Apple.java,HelloWorld.java;
```java
public class Apple {
private String id;
getter/setter.....
}
```
HelloWorld.java 创建Apple的对象用Class.forName指定类加载器为TCCL的类
```java
public class HelloWorld {
public String test() throws Exception {
StringBuilder sb = new StringBuilder();
ClassLoader tccl = Thread.currentThread().getContextClassLoader();
// appleClass指定类加载器为tccl
Class appleClass = Class.forName("com.xxx.common.Apple", true, tccl);
sb.append("thread_context_classLoader:" + tccl.toString() + "\n");
sb.append("Apple_Class_classLoader:"
+appleClass.getClassLoader().toString()+"\n");

if(tccl.getParent()!=null){
sb.append("thread_context_classLoader_parent:"+
tccl.toString()+"\n");
}else{
sb.append("thread_context_classLoader_parent:null\n");
}
if(String.class.getClassLoader()!=null){
sb.append("String_classLoader:"
+String.class.getClassLoader().toString()+"\n");
}else{
sb.append("String_classLoader:null\n");
}
return sb.toString();
}
}
```
执行MyURLClassLoaderTest.java获取的结果是:加载Apple类的是myUrlCl类加载器,myUrlCl加载器的父类加载器是系统类加载器,和ClassLoader myUrlCl = new URLClassLoader(baseUrls,systemCl)的设置符合;加载String的是启动类加载器。输出的结果如下:
```xml
thread_context_classLoader:java.net.URLClassLoader@1c90a278
Apple_Class_classLoader:java.net.URLClassLoader@1c90a278
thread_context_classLoader_parent:sun.misc.Launcher$AppClassLoader@4e7a15b
String_classLoader:null
```
如果在HelloWorld.java中有main方法调用test方法,结果:加载Apple的是系统类加载器;加载String的是启动类加载器。输出结果如下:
```xml
thread_context_classLoader:sun.misc.Launcher$AppClassLoader@4e7a15b
Apple_Class_classLoader:sun.misc.Launcher$AppClassLoader@4e7a15b
thread_context_classLoader_parent:sun.misc.Launcher$ExtClassLoader@3125fe1
String_classLoader:null
```

栗子演示完毕,是不是有点迷惑:为什么不直接加载common-test-1.0-SNAPSHOT 这个jar包的下面的类。 这种类加载设计应用场景是什么?其中一个应用场景是Tomcat的启动类就是应用了这种特性。
![tomcat_classloader](http://7xlamb.com1.z0.glb.clouddn.com/tomcat_classloader1.jpg)
每一个WebApp Class Loader都会有个统一的启动方法start(或者init),Catalina ClassLoader(类似于上例中的MyURLCl)加载WebAppClassLoader,这样就能做到每个WebApp间接依赖的下游的类,即使类限定名一直也不会发生冲突,这就是类隔离的。
> JVM唯一标识一个类:类加载器+类限定名

有一个点留给读者思考,如果MyURLClassLoaderTest中注释掉以下代码,执行结果是怎么样的
``` java
Thread.currentThread().setContextClassLoader(myUrlCl);
```

Java类加载器详解的更多相关文章

  1. [转载] Java高新技术第一篇:类加载器详解

    本文转载自: http://blog.csdn.net/jiangwei0910410003/article/details/17733153 首先来了解一下字节码和class文件的区别: 我们知道, ...

  2. Java高新技术第一篇:类加载器详解

    首先来了解一下字节码和class文件的区别: 我们知道,新建一个Java对象的时候,JVM要将这个对象对应的字节码加载到内存中,这个字节码的原始信息存放在classpath(就是我们新建Java工程的 ...

  3. JAVA类加载机制详解

    “代码编译的结果从本地机器码转变为字节码,是存储格式发展的一小步,却是变成语言发展的一大步”,这句话出自<深入理解JAVA虚拟机>一书,后面关于jvm的系列文章主要都是参考这本书. JAV ...

  4. Java---JUnita、注解与类加载器详解以及实例

    JUnit软件测试技术(工具) 在项目中建立专门用户测试的包结构. 在Junit中,通过@Test注解,可以运行一个方法. ★ Junit注解说明 使用了@Test注解应该满足以下条件: 1) 必须是 ...

  5. Java 类加载机制详解

    一.类加载器 类加载器(ClassLoader),顾名思义,即加载类的东西.在我们使用一个类之前,JVM需要先将该类的字节码文件(.class文件)从磁盘.网络或其他来源加载到内存中,并对字节码进行解 ...

  6. 类加载器详解 (转至http://blog.csdn.net/jiangwei0910410003/article/details/17733153)

    首先来了解一下字节码和class文件的区别: 我们知道,新建一个java对象的时候,JVM要将这个对象对应的字节码加载到内存中,这个字节码的原始信息存放在classpath(就是我们新建Java工程的 ...

  7. Java反射机制详解

    Java反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法:对于任意一个对象,都能够调用它的任意一个方法和属性:这种动态获取的信息以及动态调用对象的方法的功能称为Java语言的反 ...

  8. Java程序员的必备知识-类加载机制详解

    类加载器的概念 类加载器是一个用来加载类文件的类. Java源代码通过javac编译器编译成类文件.然后JVM来执行类文件中的字节码来执行程序.类加载器负责加载文件系统.网络或其他来源的类文件. JV ...

  9. JVM类加载机制详解(二)类加载器与双亲委派模型

    在上一篇JVM类加载机制详解(一)JVM类加载过程中说到,类加载机制的第一个阶段加载做的工作有: 1.通过一个类的全限定名(包名与类名)来获取定义此类的二进制字节流(Class文件).而获取的方式,可 ...

随机推荐

  1. Yum -userguide

    Introduction Yum(Yellow dog Updater, Modified) is an automatic updater and package installer/remover ...

  2. 《c#入门经典第五版》简介及pdf电子书网盘下载地址(收藏)

    <C#入门经典(第5版)>全面讲解C# 2010和.net架构编程知识,为您编写卓越C# 2010程序奠定坚实基础.C#入门经典系列是屡获殊荣的C#名著和超级畅销书.最新版的<C#入 ...

  3. Linux必备 -- 如何在Mac OS 使用VMware 安装Linux

    Linux简介 Linux是一套免费使用和自由传播的类Unix操作系统,是一个基于POSIX和UNIX的多用户.多任务.支持多线程和多CPU的操作系统.它能运行主要的UNIX工具软件.应用程序和网络协 ...

  4. Ackerman 函数 (双递归函数)

    public static int ackerman(int n,int m){  if(n==1&&m==0){return 2;}  else if(n==0&&m ...

  5. 跟着刚哥梳理java知识点——流程控制(六)

    分支结构(if…else .switch) 1.if else 语句格式 if(条件表达式){ 执行代码块; } else if(条件表达式){ 执行代码块; } else{ 执行代码块; } 2.s ...

  6. linux入门之用户管理

    用户管理 添加用户 以root用户执行 adduser  或 useradd [new_account] -u  UID   -d 指定家目录 -g GID 指定一个基本组ID   -G指定一个附加组 ...

  7. Elasticsearch高级搜索排序( 中文+拼音+首字母+简繁转换+特殊符号过滤)

    一.先摆需求: 1.中文搜索.英文搜索.中英混搜   如:"南京东路","cafe 南京东路店" 2.全拼搜索.首字母搜索.中文+全拼.中文+首字母混搜   如 ...

  8. php写流程管理

    流程控制即某个人发起一个流程,通过一层一层审核,通过后,完成整个流程,若有一层审核未通过,中断整个流程.即结束! 比如请假流程: 某一员工发起一个请假流程,那么这个流程的节点人员即他的上级,上上级,上 ...

  9. 使用Entity Framework时遇到的问题

    1.运行程序时提示 ,vension does not match. 差不多是这样一个提示,具体怎么样的给忘记了. #1remove 'entity framework' from reference ...

  10. wow.js中各种特效对应的类名

    一.(页面在向下滚动的时候,有些元素会产生细小的动画效果.虽然动画比较小,但却能吸引你的注意.) 刚知道wow.js这个插件,之前访问别的网站下拉滚动条会出现各种效果感觉特别神奇,现在自己依葫芦画瓢也 ...