一、ClassLoader概念

  ClassLoader是用来动态的加载class文件到虚拟机中,并转换成java.lang.class类的一个实例,每个这样的实例用来表示一个java类,我们可以根据Class的实例得到该类的信息,并通过实例的newInstance()方法创建出该类的一个对象,除此之外,ClassLoader还负责加载Java应用所需的资源,如图像文件和配置文件等。

  ClassLoader类是一个抽象类。如果给定类的二进制名称,那么类加载器会试图查找或生成构成类定义的数据。一般策略是将名称转换为某个文件名,然后从文件系统读取该名称的“类文件”。ClassLoader类使用委托模型来搜索类和资源。每个 ClassLoader实例都有一个相关的父类加载器。需要查找类或资源时,ClassLoader实例会在试图亲自查找类或资源之前,将搜索类或资源的任务委托给其父类加载器。

  注意:程序在启动的时候,并不会一次性加载程序所要用的所有class文件,而是根据程序的需要,通过Java的类加载机制来动态加载某个class文件到内存中。

二、JVM平台提供三层classLoader

  1. Bootstrap classLoader:采用native code实现,是JVM的一部分,主要加载JVM自身工作需要的类,如java.lang.*、java.uti.*等; 这些类位于$JAVA_HOME/jre/lib/rt.jar。Bootstrap ClassLoader不继承自ClassLoader,因为它不是一个普通的Java类,底层由C++编写,已嵌入到了JVM内核当中,当JVM启动后,Bootstrap ClassLoader也随着启动,负责加载完核心类库后,并构造Extension ClassLoader和App ClassLoader类加载器。
  2. ExtClassLoader:扩展的class loader,加载位于$JAVA_HOME/jre/lib/ext目录下的扩展jar。
  3. AppClassLoader:系统class loader,父类是ExtClassLoader,加载$CLASSPATH下的目录和jar;它负责加载应用程序主函数类。

  其体系结构图如下:

  

  如果要实现自己的类加载器,不管是实现抽象列ClassLoader,还是继承URLClassLoader类,它的父加载器都是AppClassLoader,因为不管调用哪个父类加载器,创建的对象都必须最终调用getSystemClassLoader()作为父加载器,getSystemClassLoader()方法获取到的正是AppClassLoader。

  注意:Bootstrap classLoader并不属于JVM的等级层次,它不遵守ClassLoader的加载规则,Bootstrap classLoader并没有子类。

三、JVM加载class文件到内存有两种方式

  1. 隐式加载:不通过在代码里调用ClassLoader来加载需要的类,而是通过JVM来自动加载需要的类到内存,例如:当类中继承或者引用某个类时,JVM在解析当前这个类不在内存中时,就会自动将这些类加载到内存中。
  2. 显示加载:在代码中通过ClassLoader类来加载一个类,例如调用this.getClass.getClassLoader().loadClass()或者Class.forName()。

四、ClassLoader加载类的过程

  1. 找到.class文件并把这个文件加载到内存中
  2. 字节码验证,Class类数据结构分析,内存分配和符号表的链接
  3. 类中静态属性和初始化赋值以及静态代码块的执行

五、自定义类加载器

  1、为何要自定义类加载器?

  JVM提供的类加载器,只能加载指定目录的jar和class,如果我们想加载其他位置的类或jar时,例如加载网络上的一个class文件,默认的ClassLoader就不能满足我们的需求了,所以需要定义自己的类加载器。

  2、如何实现自定义的类加载器?

  我们实现一个ClassLoader,并指定这个ClassLoader的加载路径。有两种方式:

  方式一:继承ClassLoader,重写父类的findClass()方法,代码如下:

 
 1 import java.io.ByteArrayOutputStream;
2 import java.io.File;
3 import java.io.FileInputStream;
4 import java.io.IOException;
5 public class PathClassLoader extends ClassLoader
6 {
7 public static final String drive = "d:/";
8 public static final String fileType = ".class";
9
10 public static void main(String[] args) throws Exception
11 {
12 PathClassLoader loader = new PathClassLoader();
13 Class<?> objClass = loader.loadClass("HelloWorld", true);
14 Object obj = objClass.newInstance();
15 System.out.println(objClass.getName());
16 System.out.println(objClass.getClassLoader());
17 System.out.println(obj.getClass().toString());
18 }
19
20 public Class<?> findClass(String name)
21 {
22 byte[] data = loadClassData(name);
23 return defineClass(name, data, 0, data.length);// 将一个 byte 数组转换为 Class// 类的实例
24 }
25 public byte[] loadClassData(String name)
26 {
27 FileInputStream fis = null;
28 byte[] data = null;
29 try
30 {
31 fis = new FileInputStream(new File(drive + name + fileType));
32 ByteArrayOutputStream baos = new ByteArrayOutputStream();
33 int ch = 0;
34 while ((ch = fis.read()) != -1)
35 {
36 baos.write(ch);
37 }
38 data = baos.toByteArray();
39 } catch (IOException e)
40 {
41 e.printStackTrace();
42 }
43 return data;
44 }
45 }
 

  在第13行,我们调用了父类的loadClass()方法,该方法使用指定的二进制名称来加载类,下面是loadClass方法的源代码:

 
protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException
{
synchronized (getClassLoadingLock(name))
{
// 第一步先检查这个类是否已经被加载
Class<?> c = findLoadedClass(name);
if (c == null)
{
long t0 = System.nanoTime();
try
{
//parent为父加载器
if (parent != null)
{
//将搜索类或资源的任务委托给其父类加载器
c = parent.loadClass(name, false);
} else
{
//检查该class是否被BootstrapClassLoader加载
c = findBootstrapClassOrNull(name);
}
}
catch (ClassNotFoundException e)
{
// ClassNotFoundException thrown if class not found
// from the non-null parent class loader
}
if (c == null)
{
//如果上述两步均没有找到加载的class,则调用findClass()方法
long t1 = System.nanoTime();
c = findClass(name);
// this is the defining class loader; record the stats
sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
sun.misc.PerfCounter.getFindClasses().increment();
}
}
if (resolve)
{
resolveClass(c);
}
return c;
}
}
 

  这个方法首先检查指定class是否已经被加载,如果已被加载过,则调用resolveClass()方法链接指定的类,如果还未加载,则先将搜索类或资源的任务委托给其父类加载器,检查该class是否被BootstrapClassLoader加载,如果上述两步均没有找到加载的class,则调用findClass()方法,在我们自定义的加载器中,我们重写了findClass方法,去我们指定的路径下加载class文件。

  另外,我们自定义的类加载器没有指定父加载器,在JVM规范中不指定父类加载器的情况下,默认采用系统类加载器即AppClassLoader作为其父加载器,所以在使用该自定义类加载器时,需要加载的类不能在类路径中,否则的话根据双亲委派模型的原则,待加载的类会由系统类加载器加载。如果一定想要把自定义加载器需要加载的类放在类路径中, 就要把自定义类加载器的父加载器设置为null。

  方式二:继承URLClassLoader类,然后设置自定义路径的URL来加载URL下的类。

  我们将指定的目录转换为URL路径,然后重写findClass方法。

六、实现类的热部署

  1、什么是类的热部署?

  所谓热部署,就是在应用正在运行的时候升级软件,不需要重新启用应用。

  对于Java应用程序来说,热部署就是运行时更新Java类文件。在基于Java的应用服务器实现热部署的过程中,类装入器扮演着重要的角色。大多数基于Java的应用服务器,包括EJB服务器和Servlet容器,都支持热部署。

  类装入器不能重新装入一个已经装入的类,但只要使用一个新的类装入器实例,就可以将类再次装入一个正在运行的应用程序。

  2、如何实现Java类的热部署

  前面的分析,我们已经知道,JVM在加载类之前会检查请求的类是否已经被加载过来,也就是要调用findLoadedClass方法查看是否能够返回类实例。如果类已经加载过来,再调用loadClass会导致类冲突。

  但是,JVM判断一个类是否是同一个类有两个条件:一是看这个类的完整类名是否一样(包括包名),二是看加载这个类的ClassLoader加载器是否是同一个(既是是同一个ClassLoader类的两个实例,加载同一个类也会不一样)。

  所以,要实现类的热部署可以创建不同的ClassLoader的实例对象,然后通过这个不同的实例对象来加载同名的类。

七、参考资料

  1、http://www.2cto.com/kf/201403/284030.html

标签: Java

ClassLoader工作机制的更多相关文章

  1. 深入理解ClassLoader工作机制(jdk1.8)

    ClassLoader 顾名思义就是类加载器,ClassLoader 作用: 负责将 Class 加载到 JVM 中    审查每个类由谁加载(父优先的等级加载机制)    将 Class 字节码重新 ...

  2. 理解ClassLoader工作机制

    package com.ioc; public class Test { public static void main(String[] args) throws ClassNotFoundExce ...

  3. ClassLoader 工作机制

    ClassLoader 采用上级委托接待机制加载 class JVM 平台提供三层 ClassLoader 1.Bootstrap ClassLoader:主要加载 JVM 自身工作需要的类 2.Ex ...

  4. 第六章 深入分析ClassLoader工作机制

    补充(非书中): Java 源程序(.java 文件)在经过 Java 编译器编译之后就被转换成 Java 字节代码(.class 文件).类加载器负责读取Java字节代码,并转换成 java.lan ...

  5. Java Web ClassLoader工作机制

    一.ClassLoader的作用: 1.类加载机制:父优先的等级加载机制 2.类加载过程 3.将Class字节码重新解析成JVM统一要求的对象格式 二.ClassLoader常用方法 1.define ...

  6. Java Web 深入分析(5) Java ClassLoader 工作机制

    Classloader 有3个作用 将class加载到JVM中去 审查每个类由谁去加载,是一种父优先的等级加载 把Class字节码统一编译成JVM统一要求的对象格式 ClassLoader的等级加载机 ...

  7. java虚拟机学习-慢慢琢磨JVM(2-1)ClassLoader的工作机制

    ClassLoader的工作机制 java应用环境中不同的class分别由不同的ClassLoader负责加载. 一个jvm中默认的classloader有Bootstrap ClassLoader. ...

  8. ClassLoader的工作机制

    本文中主要介绍类加载器的工作机制 一:首先什么是类加载器? 类加载器就是用来加载java类到java虚拟机中.java源程序经过编译之后形成字节码文件,类加载器将字节码文件加载到内存中,并转换成jav ...

  9. JVM结构、GC工作机制详解

      JVM结构.内存分配.垃圾回收算法.垃圾收集器.下面我们一一来看. 一.JVM结构 根据<java虚拟机规范>规定,JVM的基本结构一般如下图所示: 从左图可知,JVM主要包括四个部分 ...

随机推荐

  1. 开源CMS的忠实粉丝——We7

    说到开源CMS这个词,首先来说一下什么是开源,因为很多人可能会存在一个误区,开源就是免费使用,其实不然.开源产品,从事软件开发的专业人士都很清楚,开源就是开发源码,是把一个软件的开发过程中的技术结构, ...

  2. [译] ASP.NET 生命周期 – ASP.NET 请求生命周期(二)

    ASP.NET 请求生命周期 全局应用类也可以用来跟踪每个独立请求的生命周期,包括请求从 ASP.NET 平台传递到 MVC 框架.ASP.NET 框架会创建一个定义在 Global.asax 文件中 ...

  3. SQL 跨服务器数据库增、删、改、查(一)

    --开启本服务器中操作其他服务器的功能 reconfigure --输出消息 reconfigure --输出消息 --增 INSERT INTO OPENROWSET('SQLOLEDB','jx3 ...

  4. android项目在eclipse下编译运行的问题

    JDK与电脑系统要匹配,都是32位或者64位: android工程要与JDK相匹配,如果之前的android工程使用的jdk版本较高,则可能出现一些包或者类.方法.属性对应不上而报错,Android ...

  5. hdu 4740 The Donkey of Gui Zhou(暴力搜索)

    题目地址:http://acm.hdu.edu.cn/showproblem.php?pid=4740 [题意]: 森林里有一只驴和一只老虎,驴和老虎互相从来都没有见过,各自自己走过的地方不能走第二次 ...

  6. json分别算出元素的个数和最多的元素

    个数: var str = 'aaafsdsaaasasasasaa'; var json = {}; for (var i = 0; i < str.length; i++) { if(!js ...

  7. 彻底弄懂LSH之simHash算法

    马克·吐温曾经说过,所谓经典小说,就是指很多人希望读过,但很少人真正花时间去读的小说.这种说法同样适用于“经典”的计算机书籍. 最近一直在看LSH,不过由于matlab基础比较差,一直没搞懂.最近看的 ...

  8. Binary search for the first element greater than target

    We all know how to search through an array for an element whose value equals the target value, but h ...

  9. 安装Redis完整过程

    概述    首先报告一下我系统的版本: [root@firefish init.d]# cat /etc/issue 系统版本信息如下: 引用 CentOS release 6.4 (Final) K ...

  10. HDU1431+简单题

    题意简单 预处理之后会发现符合条件的数最多781个... 所以打表.. /* */ #include<algorithm> #include<iostream> #includ ...