定义

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

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

正因为类加载器的功能角色如此重要,因此虚拟机对其的实现规范也十分重视。在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. Linux(二)——常用命令

    一.一般指令 立刻关机:shutdown -h now 1分钟后关机:shutdown -h 1 立刻重启:shutdown -r now 立刻关机:halt 立刻重启:reboot 把内存的数据同步 ...

  2. java基础之ThreadLocal

    早在JDK 1.2的版本中就提供java.lang.ThreadLocal,ThreadLocal为解决多线程程序的并发问题提供了一种新的思路.使用这个工具类可以很简洁地编写出优美的多线程程序.Thr ...

  3. Pikachu靶场通关之XSS(跨站脚本)

    一.XSS(跨站脚本)概述 Cross-Site Scripting 简称为"CSS",为避免与前端叠成样式表的缩写"CSS"冲突,故又称XSS.一般XSS可以 ...

  4. 【Nginx】Linux常用命令------启动、停止、重启

    启动 启动代码格式:nginx安装目录地址 -c nginx配置文件地址 例如: [root@LinuxServer sbin]# /usr/local/nginx/sbin/nginx -c /us ...

  5. 5UCMS判断当前栏目高亮(用于当前所在栏目加背景图片或颜色)

    5UCMS判断当前栏目高亮标签 比较简单的是频道页(channel.html): 大类代码: <!--menu:{ $row=10 $table=channel }--> <li { ...

  6. Java基础系列(33)- 计算器

    package method; import java.util.Scanner; public class Demo09 { static double result; static String ...

  7. css宽度+字体+颜色+边框+文本+光标+伪类选择器

    常用属性: width:宽 height:高 min-width:最小宽度 :可以设置如果宽度变小了,有个滑动效果(常常在我们布局的过程中需要去设置) min-height;最小高度 max-widt ...

  8. Faster RCNN 改进论文及资料

    1,面向小目标的多尺度Faster RCNN检测算法 黄继鹏等 对高分辨率图像进行下采样和上采样,使得网上获取的数据与实际测试数据分布接近. 下采样:最大池化和平均池化 上采样:线性插值,区域插值,最 ...

  9. mybatis if else if 条件判断SQL片段表达式取值和拼接

    前言 最近在开发项目的时候涉及到复杂的动态条件查询,但是mybaits本身不支持if elseif类似的判断但是我们可以间接通过 chose when otherwise 去实现其中choose为一个 ...

  10. 字体小于12px 无法缩小解决方案

    通过缩放进行大小控制. 缩放可能会导致元素也进行缩放.需要注意 transform: scale(0.5);