定义

前面说过加载阶段是一个可以让设计人员高度自控的模块,因为类文件的源头可以是多种多样的,代码生成、反射生成或从网络中生成等。因此类加载器作为对这些文件的处理就显得尤为重要。

但类加载器的功能不仅如此,其还有一个重要的功能就是和一个类的全限定名唯一确定一个类。通俗来说,要说两个类是相同的,不仅其全限定名要一样,其对应的类加载器也必须相同,才能说明两个类是相等的。

正因为类加载器的功能角色如此重要,因此虚拟机对其的实现规范也十分重视。在Java虚拟机中,对其的实现模型是双亲委派模型

模型

双亲委派模型的主要执行过程示意图如上所示,其分为启动类加载器(Bootstrap Class-loader),拓展类加载器(Extension Class-loader),应用程序类加载(Application Class-loader)。

其中启动类加载器主要负责加载 JRE 的核心类库,如 JRE 目录下的 rt.jar。但其实根据《深入分析 Java Web 技术内幕》上所说,启动类加载器并不严格符合双亲委派模型,因为Bootstrap Class-loader 并不属于 JVM 的类等级层次。Bootstrap Class-loader 是没有子类的,Extension Class-loader 也是没有父类的。不过在这里我们并不深究,只要知道有这一点就可以了。

Extension Class-loader 主要负责加载 JRE 拓展目录 ext 下的类。

Application Class-loader 主要负责用户类路径(Class-path)下的类,这个类加载器是使用的最多的,因为大大多数情况下,一般开发者并没有实现自定义的类加载器,那么 JVM 就会使用这个来加载类大部分类。

执行过程

上图就是双亲委派模型的执行过程,当类开始加载的时候,先检查是否已经被加载过,如果没有被加载过,则调用父类的加载方法,如果父类加载失败,抛出异常,则调用自身的 findClass() 方法进行加载。

JDK 中加载过程的源码分析:

protected Class<?> loadClass(String name,boolean resolve) throws ClassNotFoundException
{
synchronized (getClassLoadingLock(name)) {
// First,check if the class has already been loaded
// 如果加载过了,就不要加载直接返回
Class c = findLoadedClass(name);
if (c == null) {
long t0 = System.nanoTime();
try {
// 判断是否有父加载器
if (parent != null) {
// 有父加载器则调用父加载器加载
c = parent.loadClass(name, false);
} else {
// 无父亲就调用 bootstarp 加载器来加载
c = findBootstrapClassOrNull(name);
}
} catch (ClassNotFoundException e) {
// ClassNotFoundException thrown if class not found
// from the non-null parent class loader
}
// 父加载器和 bootstarp 加载器都没有找到指定类,调用当前类的 findClass() 来完成类加载
// 因此,自定义类加载器,就是重写 findClass() 方法
if (c == null) {
// If still not found, then invoke findClass in order
// to find the class.
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;
}
}

从源码中,我们可以看到其实符合规范要求的 双亲委派模型 的。而当我们要自定义一个类加载器的时候就是通过重写 findClass() 来实现的。

自定义类加载器

/**
* 1. 自定义类加载器通过集成ClassLoader来实现,主要通过重写findClass方法
* 2. findClass方法首先通过自定义的loadByte()方法将Class文件转换成byte[]字节流
* 3. 然后通过defineClass()方法将其转换为Class对象
*/
public class SelfClassLoader extends ClassLoader {
private String classPath;
public SelfClassLoader(String classPath) {
this.classPath = classPath;
} /**
* 通过 difineClass,将一个字节数组转换为Class 对象
* @param name
* @return
* @throws ClassNotFoundException
*/
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
try {
byte[] data = loadByte(name);
return defineClass(name, data, 0, data.length);
} catch (Exception e) {
e.printStackTrace();
throw new ClassNotFoundException();
}
} /**
* 根据路径将指定的文件读取为byte 流
* @param name
* @return
* @throws IOException
*/
private byte[] loadByte(String name) throws IOException {
name = name.replaceAll("\\.", "/");
FileInputStream fis = new FileInputStream(classPath + "/" + name
+ ".class");
int len = fis.available();
byte[] data = new byte[len];
fis.read(data);
fis.close();
return data;
}
}

另一个种实现自定义类加载器的方法:

/**
* 1. 加载指定packageName下的类
* 2. 用自定义类加载器进行加载,如果加载失败,再交给父加载器进行加载
*/
public class UrlSelfClassloader extends URLClassLoader {
private String packageName = ""; public UrlSelfClassloader(URL[] urls, ClassLoader parent) {
super(urls, parent);
} @Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
Class<?> aClass = findLoadedClass(name);
if (Objects.nonNull(aClass)){
return aClass;
}
if (!packageName.startsWith(name)){
return super.loadClass(name);
}else {
return findClass(name);
}
}
}

如何使用自定义的类加载器

public static void main(String args[]) throws Exception {
MyClassLoader classLoader = new MyClassLoader("");
Class clazz = classLoader.loadClass("");
Object obj = clazz.newInstance();
Method helloMethod = clazz.getDeclaredMethod("hello", null);
helloMethod.invoke(obj, null);
}

[JVM-6]类加载器的更多相关文章

  1. JVM自定义类加载器加载指定classPath下的所有class及jar

    一.JVM中的类加载器类型 从Java虚拟机的角度讲,只有两种不同的类加载器:启动类加载器和其他类加载器. 1.启动类加载器(Boostrap ClassLoader):这个是由c++实现的,主要负责 ...

  2. 【深入理解JVM】类加载器与双亲委派模型 (转)

    出处: [深入理解JVM]类加载器与双亲委派模型 加载类的开放性 类加载器(ClassLoader)是Java语言的一项创新,也是Java流行的一个重要原因.在类加载的第一阶段“加载”过程中,需要通过 ...

  3. 1.1 jvm核心类加载器--jdk源码剖析

    目录 前提: 运行环境 1. 类加载的过程 1.1 类加载器初始化的过程 1.2 类加载的过程 1.3 类的懒加载 2. jvm核心类加载器 3. 双亲委派机制 4. 自定义类加载器 5. tomca ...

  4. 【JVM】JVM之类加载器

    一.前言 首先,小小测试,看是否已经掌握了JVM类加载的过程 1.1.测试一 class Singleton { private static Singleton sin = new Singleto ...

  5. JVM之类加载器下篇

    除了自定义的类加载之外,jvm存在三种类加载器,并以一种父委托的加载机制进行加载. --启动类加载器,又称根加载器,是一个native的方法,使用c++实现.在java中我们用null标识,用于加载j ...

  6. 【深入理解JVM】类加载器与双亲委派模型

    原文链接:http://blog.csdn.net/u011080472/article/details/51332866,http://www.cnblogs.com/lanxuezaipiao/p ...

  7. JVM学习一:JVM之类加载器概况

    18年转眼就3月份都快结束了,也就是说一个季度就结束了:而我也因为年前笔记本坏了,今天刚修好了,那么也应该继续学习和博客之旅了.今年的博客之旅,从JVM开始学起,下面我们就言归正传,进入正题. 一.J ...

  8. (转)JVM——自定义类加载器

    背景:为什么要自定义,如何自定义,实现过程 转载:http://blog.csdn.net/SEU_Calvin/article/details/52315125 0. 为什么需要自定义类加载器 网上 ...

  9. JVM虚拟机-类加载器子系统

    转自博客:http://www.cnblogs.com/muffe/p/3541189.html   还有一些自己补充的知识点 一.类加载器基本概念 顾名思义,类加载器(class loader)用来 ...

  10. 深入理解JVM一类加载器原理

    我们知道我们编写的java代码,会经过编译器编译成字节码文件(class文件),再把字节码文件装载到JVM中,映射到各个内存区域中,我们的程序就可以在内存中运行了.那么字节码文件是怎样装载到JVM中的 ...

随机推荐

  1. redux的使用

    1.redux的使用 核心概念 action 动作的对象 包含2个属性 type:标识属性, 值为字符串, 唯一, 必要属性 data:数据属性, 值类型任意, 可选属性 例子:{ type: 'AD ...

  2. git,github,webstrom配置

    在使用 WebStorm 上传本地项目到 GitHub 之前,先要做一些相关配置. 首先打开 WebStorm ,依次点击File -> Settings... 打开系统设置面板,在上面搜索 g ...

  3. leaflet加载离线OSM(OpenStreetMap)

    本文为博主原创,如需转载需要署名出处. leaflet作为广为应用的开源地图操作的API,是非常受欢迎,轻量级的代码让使用者更容易操作. 废话不多说,下面直接给出范例. 首先在这个网站下载leafle ...

  4. Servlet 之文件下载

    Servlet 之文件下载 import javax.servlet.ServletException; import javax.servlet.ServletOutputStream; impor ...

  5. Git入门配置

    1.账户注册: 无论是GitHub还是码云(下称Gitee),要使用他们,我们都需要先注册账户,已有账户的可以跳过此步骤. Gitee GitHub 2.创建仓库: a.创建远程仓库 登入Gitee后 ...

  6. JS 之 每日一题 之 算法 ( 划分字母区间 )

    题目详解: 字符串 S 由小写字母组成.我们要把这个字符串划分为尽可能多的片段,同一个字母只会出现在其中的一个片段.返回一个表示每个字符串片段的长度的列表. 例子: 示例 1: 输入:S = &quo ...

  7. GridControl常用操作

    显示分组面板 gridView1.OptionsView.ShowGroupPanel = true; 是否开启多选 gridView1.OptionsSelection.MultiSelect = ...

  8. 如何解决浮动元素高度塌陷---CSS

    解决高度塌陷问题的方法: 方法一. //给父元素添加声明 overflow:hidden; 缺点:回隐藏溢出的元素: 方法二. 在浮动的元素下添加空div标签,并给该元素添加声明: clear:bot ...

  9. vue-自定义指令(directive )的使用方法

    前言 在vue项目中我们经常使用到 v-show ,v-if,v-for等内置的指令,除此之外vue还提供了非常方便的自定义指令,供我们对普通的dom元素进行底层的操作.使我们的日常开发变得更加方便快 ...

  10. Mysql backup and Recovery Data Type.

    数据库备份方法: 备份类型:物理备份和逻辑备份: 物理备份是指直接复制存储数据库内容的目录和文件,这种类型的备份适用于出现问题时需要快速恢复的大型重要数据库.逻辑备份保存以逻辑数据库结构(create ...