[JVM-6]类加载器
定义
前面说过加载阶段是一个可以让设计人员高度自控的模块,因为类文件的源头可以是多种多样的,代码生成、反射生成或从网络中生成等。因此类加载器作为对这些文件的处理就显得尤为重要。
但类加载器的功能不仅如此,其还有一个重要的功能就是和一个类的全限定名唯一确定一个类。通俗来说,要说两个类是相同的,不仅其全限定名要一样,其对应的类加载器也必须相同,才能说明两个类是相等的。
正因为类加载器的功能角色如此重要,因此虚拟机对其的实现规范也十分重视。在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]类加载器的更多相关文章
- JVM自定义类加载器加载指定classPath下的所有class及jar
一.JVM中的类加载器类型 从Java虚拟机的角度讲,只有两种不同的类加载器:启动类加载器和其他类加载器. 1.启动类加载器(Boostrap ClassLoader):这个是由c++实现的,主要负责 ...
- 【深入理解JVM】类加载器与双亲委派模型 (转)
出处: [深入理解JVM]类加载器与双亲委派模型 加载类的开放性 类加载器(ClassLoader)是Java语言的一项创新,也是Java流行的一个重要原因.在类加载的第一阶段“加载”过程中,需要通过 ...
- 1.1 jvm核心类加载器--jdk源码剖析
目录 前提: 运行环境 1. 类加载的过程 1.1 类加载器初始化的过程 1.2 类加载的过程 1.3 类的懒加载 2. jvm核心类加载器 3. 双亲委派机制 4. 自定义类加载器 5. tomca ...
- 【JVM】JVM之类加载器
一.前言 首先,小小测试,看是否已经掌握了JVM类加载的过程 1.1.测试一 class Singleton { private static Singleton sin = new Singleto ...
- JVM之类加载器下篇
除了自定义的类加载之外,jvm存在三种类加载器,并以一种父委托的加载机制进行加载. --启动类加载器,又称根加载器,是一个native的方法,使用c++实现.在java中我们用null标识,用于加载j ...
- 【深入理解JVM】类加载器与双亲委派模型
原文链接:http://blog.csdn.net/u011080472/article/details/51332866,http://www.cnblogs.com/lanxuezaipiao/p ...
- JVM学习一:JVM之类加载器概况
18年转眼就3月份都快结束了,也就是说一个季度就结束了:而我也因为年前笔记本坏了,今天刚修好了,那么也应该继续学习和博客之旅了.今年的博客之旅,从JVM开始学起,下面我们就言归正传,进入正题. 一.J ...
- (转)JVM——自定义类加载器
背景:为什么要自定义,如何自定义,实现过程 转载:http://blog.csdn.net/SEU_Calvin/article/details/52315125 0. 为什么需要自定义类加载器 网上 ...
- JVM虚拟机-类加载器子系统
转自博客:http://www.cnblogs.com/muffe/p/3541189.html 还有一些自己补充的知识点 一.类加载器基本概念 顾名思义,类加载器(class loader)用来 ...
- 深入理解JVM一类加载器原理
我们知道我们编写的java代码,会经过编译器编译成字节码文件(class文件),再把字节码文件装载到JVM中,映射到各个内存区域中,我们的程序就可以在内存中运行了.那么字节码文件是怎样装载到JVM中的 ...
随机推荐
- list类型数据的操作指令
1. 结果是 3 2 1 还可以继续追加如下: 2. 3. 4.删除表头元素(最左侧的元素),并返回该元素 5. 6. 7.删除表尾的元素(最右侧的元素),并返回该元素 8.
- 迭代器生成器阅读【Python3.8官网文档】
目录 1.迭代器 2.生成器 2.1.yield 表达式 2.2.简要理解概念 2.3.生成器-迭代器的方法 2.4.生成器理解 2.5.例子 3.生成器表达式 1.迭代器 for element i ...
- 异步处理方式之信号(一):基础知识和signal函数说明
文章目录 1. 引言 2. 信号的概念 2.1 信号操作之忽略信号 2.2 信号操作之捕捉信号 2.3 信号操作之执行系统默认操作 2.4 常见的信号 3. 函数signal 3.1 signal函数 ...
- 三剑客之awk 逐行读取
目录: 一.awk工作原理 二.按行输出文本 三.按字段输出文本 四.通过管道,双引号调用shall命令 五.CPU使用率 六.使用awk 统计 httpd 访问日志中每个客户端IP的出现次数 一.a ...
- python中的getpass模块问题,在pycharm中不能继续输入密码
python中getpass模块 在pycharm中运行下面的代码: 1 import getpass 2 name = input('请输入你的名字:') 3 passwd = getpass. ...
- JavaScript循环 — for、for/in、while、do/while
for 多次遍历代码块 const array = []for (var i = 0; i < 5; i++) { array.push(i)}console.log(array) // [0, ...
- 洛谷P1125——笨小猴(简易模拟)
https://www.luogu.org/problem/show?pid=1125 题目描述 笨小猴的词汇量很小,所以每次做英语选择题的时候都很头疼.但是他找到了一种方法,经试验证明,用这种方法去 ...
- Centos6.8阿里云linux系统下配置LAMP运行环境-mysql5.6
1.Apache #安装apache软件 yum -y install httpd #启动httpd服务 service httpd start #设置开机启动chkconfig --list htt ...
- Oracle基本入门
一.数据的存储 1.java 程序中的对象:数组.集合保存.当运行的程序结束的时候,里面的数据就消亡. 2.文件存储系统: 存在的缺陷: 2.1)没有明确的数据类型划分. 2.2)没有用户身份验证机制 ...
- django 自定义auth中user登陆认证以及自写认证
第一种: 重写自定义auth中user登陆认证模块, 引入MobelBackend from django.contrib.auth.backends import ModelBackend 重写验证 ...