首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >Java类加载器详解(上)

Java类加载器详解(上)

作者头像
Java团长
发布于 2018-07-23 11:18:20
发布于 2018-07-23 11:18:20
4280
举报

首先来了解一下字节码和class文件的区别:

我们知道,新建一个Java对象的时候,JVM要将这个对象对应的字节码加载到内存中,这个字节码的原始信息存放在classpath(就是我们新建Java工程的bin目录下)指定的目录下的.class文件,类加载需要将.class文件导入到硬盘中,经过一些处理之后变成字节码在加载到内存中。

下面来看一下简单的例子:

  1. package com.loadclass.demo;
  2. import java.util.Date;
  3. import java.util.List;
  4. /**
  5. * 测试类
  6. * @author Administrator
  7. */
  8. public class ClassLoaderTest {
  9. @SuppressWarnings("rawtypes")
  10. public static void main(String[] args){
  11. //输出ClassLoaderText的类加载器名称
  12. System.out.println("ClassLoaderText类的加载器的名称:"+ClassLoaderTest.class.getClassLoader().getClass().getName());
  13. System.out.println("System类的加载器的名称:"+System.class.getClassLoader());
  14. System.out.println("List类的加载器的名称:"+List.class.getClassLoader());
  15. ClassLoader cl = ClassLoaderTest.class.getClassLoader();
  16. while(cl != null){
  17. System.out.print(cl.getClass().getName()+"->");
  18. cl = cl.getParent();
  19. }
  20. System.out.println(cl);
  21. }
  22. }

输出结果:

可以看到,ClassLoaderTest类时由AppClassLoader类加载器加载的。下面就来了解一下JVM中的各个类加载器,同时来解释一下运行的结果。

Java虚拟机中类加载器:

Java虚拟机中可以安装多个类加载器,系统默认三个主要的类加载器,每个类负责加载特定位置的类:

BootStrap,ExtClassLoader,AppClassLoader

类加载器也是Java类,因为Java类的类加载器本身也是要被类加载器加载的,显然必须有第一个类加载器不是Java类,这个正是BootStrap,使用C/C++代码写的,已经封装到JVM内核中了,而ExtClassLoader和AppClassLoader是Java类。

看一下类加载器的属性结构图:

Java虚拟机中的所有类加载器采用具有父子关系的树形结构进行组织,在实例化每个类加载器对象的时候,需要为其指定一个父级类加载器对象或者默认采用系统类加载器为其父级类加载

类加载器的委托机制:

当Java虚拟机要加载第一个类的时候,到底派出哪个类加载器去加载呢?

(1). 首先当前线程的类加载器去加载线程中的第一个类(当前线程的类加载器:Thread类中有一个get/setContextClassLoader(ClassLoader cl);方法,可以获取/指定本线程中的类加载器)

(2). 如果类A中引用了类B,Java虚拟机将使用加载类A的类加载器来加载类B

(3). 还可以直接调用ClassLoader.loadClass(String className)方法来指定某个类加载器去加载某个类

每个类加载器加载类时,又先委托给其上级类加载器当所有祖宗类加载器没有加载到类,回到发起者类加载器,还加载不了,则会抛出ClassNotFoundException,不是再去找发起者类加载器的儿子,因为没有getChild()方法。例如:如上图所示: MyClassLoader->AppClassLoader->Ext->ClassLoader->BootStrap.自定定义的MyClassLoader1首先会先委托给AppClassLoader,AppClassLoader会委托给ExtClassLoader,ExtClassLoader会委托给BootStrap,这时候BootStrap就去加载,如果加载成功,就结束了。如果加载失败,就交给ExtClassLoader去加载,如果ExtClassLoader加载成功了,就结束了,如果加载失败就交给AppClassLoader加载,如果加载成功,就结束了,如果加载失败,就交给自定义的MyClassLoader1类加载器加载,如果加载失败,就报ClassNotFoundException异常,结束。

对着类加载器的层次结构图和委托加载原理,解释先前的运行的结果

因为System类,List,Map等这样的系统提供jar类都在rt.jar中,所以由BootStrap类加载器加载,因为BootStrap是祖先类,不是Java编写的,所以打印出class为null

对于ClassLoaderTest类的加载过程,打印结果也是很清楚的。

现在再来做个试验来验证上面的结论:

首先将ClassLoaderTest.java打包成.jar文件(这个步骤就不说了吧,很简单的)

然后将.jar文件拷贝到Java的安装目录中的Java/jre7/lib/ext/目录下

这时候你在运行ClassLoaderTest类,结果如下:

这时候就发现了ClassLoaderTest的类加载器变成了ExtClassLoader,这时候就说明了上面的结论是正确的,因为ExtClassLoader加载jre/ext/*.jar,首先AppClassLoader类加载器发请求给ExtClassLoader,然后ExtClassLoader发请求给BootStrap,但是BootStrap没有找到ClassLoaderTest类,所以交给ExtClassLoader处理,这时候ExtClassLoader在my_lib.jar中找到了ClassLoaderTest类,所以就把它加载了,然后结束了。

其实采用这种树形的类加载机制的好处就在于:

能够很好的统一管理类加载,首先交给上级,如果上级有了,就加载,这样如果之前已经加载过的类,这时候在来加载它的时候只要拿过来用就可以了,无需二次加载了

下面来看一下怎么定义我们自己的一个类加载器MyClassLoader:

自己可以定义类加载器,要将自己定义的类加载器挂载到系统类加载器树上,在ClassLoader的构造方法中可以指定parent,没有指定的话,就使用默认的parent

这里看一下默认的parent是使用getSystemClassLoader方法获取的,这个方法的源码没有找到,所以只能通过代码来测试一下了

System.out.println("默认的类加载器:"+ClassLoaderTest.class.getClassLoader().getSystemClassLoader());

输入结果为:

所以默认的都是将自定义的类加载器挂载到系统类加载器的最低端AppClassLoader,这个也是很合理的。

自定义的类加载器必须继承抽象类ClassLoader然后重写findClass方法,其实他内部还有一个loadClass方法和defineClass方法,这两个方法的作用是:

loadClass方法的源代码:

  1. public Class<?> loadClass(String name) throws ClassNotFoundException {
  2. return loadClass(name, false);
  3. }

再来看一下loadClass(name,false)方法的源代码:

  1. protected Class<?> loadClass(String name, boolean resolve)throws ClassNotFoundException{
  2. //加上锁,同步处理,因为可能是多线程在加载类
  3. synchronized (getClassLoadingLock(name)) {
  4. //检查,是否该类已经加载过了,如果加载过了,就不加载了
  5. Class c = findLoadedClass(name);
  6. if (c == null) {
  7. long t0 = System.nanoTime();
  8. try {
  9. //如果自定义的类加载器的parent不为null,就调用parent的loadClass进行加载类
  10. if (parent != null) {
  11. c = parent.loadClass(name, false);
  12. } else {
  13. //如果自定义的类加载器的parent为null,就调用findBootstrapClass方法查找类,就是Bootstrap类加载器
  14. c = findBootstrapClassOrNull(name);
  15. }
  16. } catch (ClassNotFoundException e) {
  17. // ClassNotFoundException thrown if class not found
  18. // from the non-null parent class loader
  19. }
  20. if (c == null) {
  21. // If still not found, then invoke findClass in order
  22. // to find the class.
  23. long t1 = System.nanoTime();
  24. //如果parent加载类失败,就调用自己的findClass方法进行类加载
  25. c = findClass(name);
  26. // this is the defining class loader; record the stats
  27. sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
  28. sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
  29. sun.misc.PerfCounter.getFindClasses().increment();
  30. }
  31. }
  32. if (resolve) {
  33. resolveClass(c);
  34. }
  35. return c;
  36. }
  37. }

在loadClass代码中也可以看到类加载机制的原理,这里还有这个方法findBootstrapClassOrNull,看一下源代码:

  1. private Class findBootstrapClassOrNull(String name)
  2. {
  3. if (!checkName(name)) return null;
  4. return findBootstrapClass(name);
  5. }

就是检查一下name是否是否正确,然后调用findBootstrapClass方法,但是findBootstrapClass方法是个native本地方法,看不到源代码了,但是可以猜测是用Bootstrap类加载器进行加载类的,这个方法我们也不能重写,因为如果重写了这个方法的话,就会破坏这种委托机制,我们还要自己写一个委托机制,很是蛋疼的。

defineClass这个方法很简单就是将class文件的字节数组编程一个class对象,这个方法肯定不能重写,内部实现是在C/C++代码中实现的

findClass这个方法就是根据name来查找到class文件,在loadClass方法中用到,所以我们只能重写这个方法了,只要在这个方法中找到class文件,再将它用defineClass方法返回一个Class对象即可。

这三个方法的执行流程是:每个类加载器:loadClass->findClass->defineClass

前期的知识了解后现在就来实现了

首先来看一下需要加载的一个类:ClassLoaderAttachment.java:

  1. package com.loadclass.demo;
  2. import java.util.Date;
  3. /**
  4. * 加载类
  5. * @author Administrator
  6. */
  7. public class ClassLoaderAttachment extends Date{
  8. private static final long serialVersionUID = 8627644427315706176L;
  9. //打印数据
  10. @Override
  11. public String toString(){
  12. return "Hello ClassLoader!";
  13. }
  14. }

这个类中输出一段话即可:编译成ClassLoaderAttachment.class

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2017-04-05,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 Java团长 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
Java类加载器详解(下)
这个类中定义了一个加密和解密的算法,很简单的,就是将字节和oxff异或一下即可,而且这个算法是加密和解密的都可以用,很是神奇呀!
Java团长
2018/07/23
6500
Java类加载器详解(下)
Java类加载器
通过一个类的全限定名来获取描述此类的二进制字节流这个动作放到Java虚拟机外部去实现,以便让应用程序自己决定如何去获取所需要的类。实现这个动作的代码模块称为“类加载器”。
@阿诚
2020/09/01
8670
Java类加载器
Java程序设计(高级及专题)- 类的加载和反射[通俗易懂]
大家好,又见面了,我是你们的朋友全栈君。 加载器 类加载器就是可以从其作用来理解,其功能就是将classpath目录下.class文件,加载到内存中来进行一些处理,处理完的结果就是一些字节码
全栈程序员站长
2022/08/04
4160
Java程序设计(高级及专题)- 类的加载和反射[通俗易懂]
深入理解Java类加载器(1):Java类加载原理解析
  每个开发人员对java.lang.ClassNotFoundExcetpion这个异常肯定都不陌生,这背后就涉及到了java技术体系中的类加载。Java的类加载机制是技术体系中比较核心的部分,虽然和大部分开发人员直接打交道不多,但是对其背后的机理有一定理解有助于排查程序中出现的类加载失败等技术问题,对理解java虚拟机的连接模型和java语言的动态性都有很大帮助。
全栈程序员站长
2022/09/22
9220
深入理解Java类加载器(1):Java类加载原理解析
类加载器的双亲委派模型_java mock 模拟接口
看下ExtClassLoader的获取方法getExtClassloader(): 可以看到ExtClassLoader是Launcher的一个内部类,继承的是URLClassLoader。
全栈程序员站长
2022/09/22
8390
类加载器的双亲委派模型_java mock 模拟接口
深入理解Java类加载器(ClassLoader)
本篇博文主要是探讨类加载器,同时在本篇中列举的源码都基于Java8版本,不同的版本可能有些许差异。主要内容如下
用户1257215
2018/10/26
1.7K0
Java魔法堂:类加载器入了个门
一、前言                               《Java魔法堂:类加载机制入了个门》中提及整个类加载流程中只有加载阶段作为码农的我们可以入手干预,其余均由JVM处理。本文将记录
^_^肥仔John
2018/01/18
8290
Java魔法堂:类加载器入了个门
深入理解Java类加载器(ClassLoader)
JVM重要的一个领域,类加载 当程序主动使用某个类时,如果该类还没被加载到内存中,则JVM就会通过加载,连接,初始化三个步骤来对类进行初始化,如果没有意外,JVM将会连续完成三个步骤,所以有时也把这个三个步骤称为类的初始化或类加载。
张哥编程
2024/12/13
5470
深入理解Java类加载器(ClassLoader)
Java中常见的类加载器及双亲委派机制的原理
我们在代码里得到类的一个class对象,然后通过它的getClassLoader方法得到一个ClassLoader对象,那么运行一下看看这个ClassLoader对象的名字是什么
全栈程序员站长
2022/07/25
2530
Java中常见的类加载器及双亲委派机制的原理
Java虚拟机--类加载器源码
类加载器源码分析 下面,我们就来深入的学习下类加载器的源码,看看到底做了哪些事情? 类加载体系 上图呈现是源码级别的类加载体系,ClassLoader是基类,所有的类加载器都需要继承它(启动类加载器除
贾博岩
2018/06/20
8570
Java学习记录--委派模型与类加载器
最近在读许令波的深入分析Java Web技术内幕一书,对于学习Java以来一直有的几个疑惑得到了解答,遂记录下来.
屈定
2018/09/27
6620
Java学习记录--委派模型与类加载器
类加载器详解
内容:转自 java知音 类加载器是负责将可能是网络上、也可能是磁盘上的class文件加载到内存中。并为其生成对应的java.lang.class对象。一旦一个类被载入JVM了,同一个类就不会被再次加载。 那么怎样才算是同一个类?在JAVA中一个类用其全限定类名(包名和类名)作为其唯一标识,但是在JVM中,一个类用其全限定类名和其类加载器作为其唯一标识。也就是说,在JAVA中的同一个类,如果用不同的类加载器加载,则生成的class对象认为是不同的。 当JVM启动时,会形成由三个类加载器组成的初始类加载器层
用户1257393
2018/01/30
7560
类加载器详解
自定义ClassLoader和双亲委派机制
博文主要讲classloader的模型、作用和使用,内容是作者学习java反射机制有关知识时记录的笔记。
静默加载
2020/05/29
1.3K0
Java 自定义类加载器教程[通俗易懂]
除了在面试中遇到类的加载器的概率会高外,在实际的工作中很少接触。但是一个程序员想要成长为大牛就必须对一些 JVM 的底层设计有些了解。在此基础上我们阅读一些源码和框架会显得更轻松。
全栈程序员站长
2022/10/04
5730
Java 自定义类加载器教程[通俗易懂]
类加载器ClassLoader
上篇文章说到,Class类可以通过一个类的全限定名去加载类,那么底层是如何去加载的呢?这就是我们今天要聊的类加载器ClassLoader,其可以通过一个类的全限定名来获取描述此类的二进制字节流,也即是将编译过后的Class文件加载到内存中。
Liusy
2020/09/01
8080
类加载器ClassLoader
ClassLoader解析(一):Java中的ClassLoader
Step1::自定义类加载器首先从缓存中查找Class是否已经加载,如果已将加载就返回该Class;如果没加载,则委托给父加载器也就是App ClassLoader。
用户1205080
2019/01/23
6480
Java中的类加载器
Class loaders属于JRE的一部分,负责在运行时将Java类动态加载到JVM。得益于class loaders,JVM在无需知晓底层文件或文件系统时就可以运行Java程序。
码代码的陈同学
2018/08/20
9570
(JVM)Java虚拟机:手把手带你深入了解类加载器(含双亲委派模型讲解)
即实现 类加载过程中“加载”环节里 “通过类的全限定名来获取定义此类的二进制字节流” 的功能
Carson.Ho
2020/01/13
4050
(JVM)Java虚拟机:手把手带你深入了解类加载器(含双亲委派模型讲解)
实习杂记(30):虚拟机类加载机制(3)
  每个开发人员对Java.lang.ClassNotFoundExcetpion这个异常肯定都不陌生,这背后就涉及到了java技术体系中的类加载。Java的类加载机制是技术体系中比较核心的部分,虽然和大部分开发人员直接打交道不多,但是对其背后的机理有一定理解有助于排查程序中出现的类加载失败等技术问题,对理解java虚拟机的连接模型和java语言的动态性都有很大帮助。
wust小吴
2019/11/14
3540
实习杂记(30):虚拟机类加载机制(3)
一看你就懂,超详细java中的ClassLoader详解
ClassLoader翻译过来就是类加载器,普通的java开发者其实用到的不多,但对于某些框架开发者来说却非常常见。理解ClassLoader的加载机制,也有利于我们编写出更高效的代码。ClassLoader的具体作用就是将class文件加载到jvm虚拟机中去,程序就可以正确运行了。但是,jvm启动的时候,并不会一次性加载所有的class文件,而是根据需要去动态加载。想想也是的,一次性加载那么多jar包那么多class,那内存不崩溃。本文的目的也是学习ClassLoader这种加载机制。
Frank909
2019/01/14
5.6K1
相关推荐
Java类加载器详解(下)
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档