JVM中ClassLoader类加载器的深入理解

JVM的体系结构图

先来看一下JVM的体系结构,如下图:

JVM的位置

JVM的位置,如下图:

JVM是运行在操作系统之上的,与硬件没有直接的交互,但是可以调用底层的硬件,用JIN(Java本地接口调用底层硬件)

JVM结构图中的class files文件

class files文件,是保存在我们电脑本地的字节码文件,.java文件经过编译之后,就会生成一个.class文件,这个文件就是class files所对应的字节码文件,如下图:

JVM结构图中的类加载器ClassLoader的解释

类加载器ClassLoader的作用

类加载器只有一个作用,就是负责把我们本地的.class字节码文件,加载到JVM中,以类模板的形式存在于JVM中。

类加载器加载的.class文件也是有要求的,并不是只要是后缀名为.class的文件,都可以被类加载器加载,这个.class文件的必须是.java文件经过编译后得到的字节码文件,也就是指这个.class文件的开头必须要是cafe babe字符单词,它的内容是经过加密后的二进制文件,但是此二进制使用十六进制表示出来的,如下图:

类加载器ClassLoader只负责加载.class文件,但至于他是否可以运行,由JVM中的执行引擎Excution Engine决定的。

解释:

Car.class是由.java文件编译得来的.class文件,存在本地磁盘。

ClassLoader:类加载器,负责加载并初始化 类文件,得到真正的Class类,即模板。

Car Class:由Car.class字节码文件,通过ClassLoader加载并且初始化得到,这个Car就是当前类的模板,这个Car Class模板就存在方法区。

car1,car2,car3:是由Car模板经过实例化而得,一个模板可以获得多个实例化对象。

拿car1举例,car1.getClass()可以得到模板Car类,Car.getClassLoader()可得到其装载器。

类加载器的种类

一共有四类。

虚拟机自带的加载器:

启动类加载器,也叫根加载器(BootStrap)。由C++编写,程序中自带的类,存储在 $JAVAHOME/jre/lib/rt.jar中,如object类等

扩展类加载器(Extension),Java编写,在我们看到的类路径中,凡是以Javax开头的,都是拓展包,存储在 $JAVAHOME/jre/lib/ext/*.jar 中,为什么会有扩展包?是因为原本的java包中在功能上,不能满足我们了,所以我们就在原本的java包的基础上扩展出了一些新的功能,而形成的新的包就叫做扩展包。

应用程序类加载器(AppClassLoader),即平时程序中自定义的类 new出来的

用户自定义的加载器:

Java.lang.ClassLoader的子类,用户可以定制类的加载方式,即如果你的程序有特殊的需求,你也可以自定义你的类加载器的加载方式 ,进入ClassLoader的源码,其为抽象类,因此在你定制化开发的时候,需要你定义自己的加载器类来继承ClassLoader抽象类即可,即 MyClassLoader extends ClassLoader

java类的加载机制

首先你需要知道,当java程序需要加载一个类的时候,会去找类加载器,然后找类加载器里面看是否有对应的类模板。

Java的类加载机制,永远是以 根加载器->拓展类加载器->应用程序类加载器 这样的一个顺序进行加载的。什么意思呢?就是如果能在BootStrapClassLoader类加载器的路径下找到对应的类模板,那么就不会再去扩展类加载器ExtensionClassLoader中找对应的类模板,如果能在扩展类加载器ExtensionClassLoader中找到对应的类模板,那么就不会去应用程序类加载器AppClassLoader中找对应的类模板。类加载器的父子级关系,如下图:

从上图可以看出根加载器BootStrapClassLoader是扩展类加载器ExtensionClassLoader的父级,扩展类加载器ExtensionClassLoader是应用程序加载器AppClassLoader的父级,如下图:

被某个类加载器加载的类,都会存放到这个类加载器的路径中,当程序需要加载某个类的时候,会先去根加载器BootstrapClassLoader的路径下,寻找是否有这个类的模板,如果没有则会去这个类加载器的下一级,若有,则不会再去下一级寻找了。

利用obj.getClass().getClassLoader()方法,可以找到对应的类模板是存放在哪个类加载器的路径下的,换一个说法,可以让你知道在加载这个类模板所对应的类的字节码文件的时候,是用的哪一个类加载器把这个类的字节码文件加载成类模板的,如下图:

那么问题来了?为什么说Object类是java自带的类呢?java自带的类到底可以在哪里找到呢?java自带的类可以 J A V A H O M E / j r e / l i b / r t . j a r 下 找 到 。 首 先 来 看 一 下 JAVAHOME/jre/lib/rt.jar下找到。首先来看一下 JAVAHOME/jre/lib/rt.jar下找到。首先来看一下JAVAHOME,也即是java的安装目录,如下图:

然后咱来看一下$JAVAHOME/jre/lib/rt.jar中的包(rt其实也即是RunTime的缩写),如下图:

打开rt.jar压缩包,它的目录结构,如下图:

Object类在java/lang包下,如下图:

JVM中的类加载器,根加载器BootStrap,在一开始,就会把rt.jar压缩包中的所有的 类的字节码文件都加载到JVM中,变成类的模板,所以这些java自带的类,对应的类的模板,都会被存放到根加载器BootStrap的路径下面。故当java程序中需要用到java中的自带类的时候,都会去根加载器BootStrap的路径下去寻找类的模板。

双亲委派机制

官方概念:当一个类收到类加载请求后,他不会首先去加载这个类,而是把这个请求委派给父类去完成。每一个层次的类加载器都是如此,因此所有的类加载器请求都是应该传到根加载器中的,只有当其父类加载器自己无法完成这个请求的时候(在他的加载路径下没有找到所需加载的class),子类加载器才会尝试自己去加载。

我的理解翻译:当java程序需要加载一个类的时候,比如要加载java.lang.String类,会首先去根加载器类BootstrapClassLoader的路径下去寻找,如果在根加载器路径下可以找到java.lang.String类,那么就不会再往下寻找java.lang.String类了,因为已经找到了;若在根加载器路径下没有找到java.lang.String类,则会去下一级扩展类加载器ExtensionClassLoader中寻找java.lang.String类,如果在扩展类加载器中找到了java.lang.String类,那么就不会再去下一级类加载器中寻找了;如果没有找到,则会去应用程序类加载器AppClassLoader中去寻找java.lang.String类,如果仍然没有找到会报classNotFoundException异常。

采用双亲委派机制的好处:保证了我们写的代码不会污染到java的源代码,因为只要java的源代码中存在我们即将要加载的类,那么java程序在加载类的时候就会去顶层的根加载器BootstrapClassLoader路径下去寻找类模板,然后把这个类加载。你就比如假设我们自己写一个java.lang包,然后在这个包里面写一个String类,那么如果java源代码中没有java.lang.String这个类,java程序在用到这个类加载的时候,会用到应用程序类加载器AppClassLoader路径下的java.lang.String类,但是,我们都知道java源代码中是有java.lang.String这个类的,也即是在根加载器BootstrapClassLoader路径下有java.lang.String这个类,所以java程序在用到这个类加载它的时候,会加载根加载器BootstrapClassLoader路径下的java.lang.String类,而不会去加载应用程序类加载器AppClassLoader下的java.lang.String类,这样就保证了,我们在java程序的其它地方所用到的java.lang.String类的方法是正确的,怎么个正确法呢?因为啊,假设你是加载的应用程序加载器AppClassLoader路径里的java.lang.String类也即是你自己写的String类,那么在java程序的其它地方如果使用到了源代码java.lang.String类里面的方法该怎么办?这样是不是会出错?这其实也就是,你写的代码污染了人家写的源代码。

双亲委派机制的程序理解,如下图:

沙箱安全机制

通过双亲委派机制,类的加载永远都是从根加载器开始的,如果跟加载器中没有对应的类模板,则会去下一级类加载器中寻找这个类模板,如果有的话则就不会去下一级寻找了。这样就保证你所写的代码不会污染Java自带的源代码,保证了沙箱的安全。

为什么说这样不会污染java自带的源代码呢?因为只要java的源代码中存在我们即将要加载的类,那么java程序在加载类的时候就会去顶层的根加载器BootstrapClassLoader路径下去寻找类模板,然后把这个类加载。你就比如假设我们自己写一个java.lang包,然后在这个包里面写一个String类,那么如果java源代码中没有java.lang.String这个类,java程序在用到这个类加载的时候,会用到应用程序类加载器AppClassLoader路径下的java.lang.String类,但是,我们都知道java源代码中是有java.lang.String这个类的,也即是在根加载器BootstrapClassLoader路径下有java.lang.String这个类,所以java程序在用到这个类加载它的时候,会加载根加载器BootstrapClassLoader路径下的java.lang.String类,而不会去加载应用程序类加载器AppClassLoader下的java.lang.String类,这样就保证了,我们在java程序的其它地方所用到的java.lang.String类的方法是正确的,怎么个正确法呢?因为啊,假设你是加载的应用程序加载器AppClassLoader路径里的java.lang.String类也即是你自己写的String类,那么在java程序的其它地方如果使用到了源代码java.lang.String类里面的方法该怎么办?这样是不是会出错?这其实也就是,你写的代码污染了人家写的源代码。

总结

到此这篇关于JVM中ClassLoader类加载器的文章就介绍到这了,更多相关JVM中ClassLoader类加载器内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • jvm之java类加载机制和类加载器(ClassLoader)的用法

    当程序主动使用某个类时,如果该类还未被加载到内存中,则JVM会通过加载.连接.初始化3个步骤来对该类进行初始化.如果没有意外,JVM将会连续完成3个步骤,所以有时也把这个3个步骤统称为类加载或类初始化. 一.类加载过程 1.加载 加载指的是将类的class文件读入到内存,并为之创建一个java.lang.Class对象,也就是说,当程序中使用任何类时,系统都会为之建立一个java.lang.Class对象. 类的加载由类加载器完成,类加载器通常由JVM提供,这些类加载器也是前面所有程序运行的基础

  • JVM中ClassLoader类加载器的深入理解

    JVM的体系结构图 先来看一下JVM的体系结构,如下图: JVM的位置 JVM的位置,如下图: JVM是运行在操作系统之上的,与硬件没有直接的交互,但是可以调用底层的硬件,用JIN(Java本地接口调用底层硬件) JVM结构图中的class files文件 class files文件,是保存在我们电脑本地的字节码文件,.java文件经过编译之后,就会生成一个.class文件,这个文件就是class files所对应的字节码文件,如下图: JVM结构图中的类加载器ClassLoader的解释 类加

  • Java中的类加载器_动力节点Java学院整理

    从java的动态性到类加载机制 Java是一种动态语言.那么怎样理解这个"动态"呢?或者说一门语言具备了什么特性,才能称之为动态语言呢?对于java,我是这样理解的. JVM(java虚拟机)执行的不是本地机器码指令,而是执行一种称之为字节码的指令(存在于class文件中).这就要求虚拟机在真正执行字节码之前,先把相关的class文件加载到内存中.虚拟机不是一次性加载所有需要的class文件,因为它在执行的时候根本不会知道以后会用到哪些class文件.它是每用到一个类,就会在运行时&q

  • classloader类加载器_基于java类的加载方式详解

    基础概念 Classloader 类加载器,用来加载 Java 类到 Java 虚拟机中.与普通程序不同的是.Java程序(class文件)并不是本地的可执行程序.当运行Java程序时,首先运行JVM(Java虚拟机),然后再把Java class加载到JVM里头运行,负责加载Java class的这部分就叫做Class Loader. JVM本身包含了一个ClassLoader称为Bootstrap ClassLoader,和JVM一样,BootstrapClassLoader是用本地代码实现

  • Java中ClassLoader类加载学习总结

    双亲委派模型 类加载这个概念应该算是Java语言的一种创新,目的是为了将类的加载过程与虚拟机解耦,达到"通过类的全限定名来获取描述此类的二进制字节流"的目的.实现这个功能的代码模块就是类加载器.类加载器的基本模型就是大名鼎鼎的双亲委派模型(Parents Delegation Model).听上去很牛掰,其实逻辑很简单,在需要加载一个类的时候,我们首先判断该类是否已被加载,如果没有就判断是否已被父加载器加载,如果还没有再调用自己的findClass方法尝试加载.基本的模型就是这样(盗图

  • Java tomcat中的类加载器和安全机制你了解吗

    目录 类加载器 双亲委派 URLClassLoader Tomcat中类加载器架构 安全机制 总结 类加载器 java中的类并不是一次加载完成的,而是按需加载.类加载器是用于加载java类到java虚拟机中的组件,它负责读取java字节码,并转换成 java.lang.Class 的一个实例,使字节码.class文件可以运行.一般类加载器负责根据一个指定的类找到对应的字节码,然后根据这些字节码定义一个java类.另外,它还可以加载资源,包括图像文件和配置文件. 类加载器可以使java类动态地加载

  • java中JVM中如何存取数据和相关信息详解

    前言: 我们每天都在编写Java代码,编译,执行.很多人已经知道Java源代码文件(.java后缀)会被Java编译器编译为字节码文件(.class后缀),然后由JVM中的类加载器加载各个类的字节码文件,加载完毕之后,交由JVM执行引擎执行. 那在整个程序执行过程中,JVM中怎么存取数据和相关信息呢? 事实上在JVM中是用一段空间来存储程序执行期间需要用到的数据和相关信息,这段空间一般被称作为Runtime Data Area(运行时数据区),也就是我们常说的JVM内存. 一.运行时数据区域包括

  • 深入解析Java中的Class Loader类加载器

    类加载的过程 类加载器的主要工作就是把类文件加载到JVM中.如下图所示,其过程分为三步: 1.加载:定位要加载的类文件,并将其字节流装载到JVM中: 2.链接:给要加载的类分配最基本的内存结构保存其信息,比如属性,方法以及引用的类.在该阶段,该类还处于不可用状态: (1)验证:对加载的字节流进行验证,比如格式上的,安全方面的: (2)内存分配:为该类准备内存空间来表示其属性,方法以及引用的类: (3)解析:加载该类所引用的其它类,比如父类,实现的接口等. 3.初始化:对类变量进行赋值. 类加载器

  • Android 中的类文件和类加载器详情

    目录 一.Java中的类加载器 二.Android中的类加载器 2.1 BootClassLoader 2.2 PathClassLoader 2.3 DexClassLoader 2.4 InMemoryDexClassLoader 三.Dex文件 3.1 Android内存中的Dex文件 3.2 Dex文件的生成 一.Java中的类加载器 首先花点时间回顾一下Java中的三种类加载器: BootStrap ClassLoader 启动类加载器,它是实现自C/C++的类加载器,用于加载JDK的

  • Java语言中的自定义类加载器实例解析

    本文研究的主要是Java语言中的自定义类加载器实例解析的相关内容,具体如下. 自己写的类加载器 需要注意的是:如果想要对这个实例进行测试的话,首先需要在c盘建立一个c://myjava的目录.然后将相应的java文件放在这个目录中.并将产生的.clas文件放在c://myjava/com/lg.test目录下,否则是找不到的.这是要注意的.. class FileClassLoader : package com.lg.test; import java.io.ByteArrayOutputSt

  • Java类加载器ClassLoader源码层面分析讲解

    目录 Launcher 源码 AppClassLoader 源码 ExtClassLoader 源码 ClassLoader 源码 总结 最终总结一下 Launcher 源码 sun.misc.Launcher类是java 虚拟机的入口,在启动 java应用 的时候会首先创建Launcher.在初始化Launcher对象的时候会创建一个ExtClassLoader拓展程序加载器 和 AppClassLoader应用程序类加载器(这俩鬼东西好像只是加载类的路径不一样而已),然后由这俩类加载器去加载

随机推荐