jvm的双亲委派机制
类加载机制及SPI

类加载机制及SPI最近重温Java类加载及双亲委派机制,并写了⼀个SPI的例⼦从⽹上找了⼀张图⽚,对着图⽚及课堂笔记来梳理下。
⾸先java⾃带的类加载器分为BootStrapClassLoader(引导\启动类加载器),ExtClassLoader(扩展类加载器),AppClassLoader(应⽤程序类加载器)三种,此外还⽀持⽤户⾃⼰定义的⾃定义类加载器,加载的是⽤户⾃⼰指定的⽬录。
BootStrapClassLoader:jvm中,c++处理类加载的这套逻辑,被称为启动类加载器,是由c++编写的,在java中为null,加载的路径是Jre/lib/rt.jar, 在这个过程中会通过启动类加载器,来加载uncherHelper,并执⾏checkAndLoadMain,以及加载main函数所在的类,并启动扩展类加载器、应⽤类加载器ExtClassLoader: 扩展类加载器,加载的是Jre/lib/ext/*.jar,查看⽅式:public static void main(String[] args) {ClassLoader classLoader = ClassLoader.getSystemClassLoader().getParent();URLClassLoader urlClassLoader = (URLClassLoader) classLoader;URL[] urls = urlClassLoader.getURLs();for (URL url : urls) {System.out.println(url);}}AppClassLoader: 应⽤类加载器,加载⽤户程序的类加载器,加载的是CLASS_PATH中指定的所有jarpublic static void main(String[] args) {String[] urls = System.getProperty("java.class.path").split(":");for (String url : urls) {System.out.println(url);}System.out.println("---------------------------------------------------------");URLClassLoader classLoader = (URLClassLoader) ClassLoader.getSystemClassLoader();URL[] urls1 = classLoader.getURLs();for (URL url : urls1) {System.out.println(url);}}双亲委派机制:类加载时,AppClassLoader 会先查看⾃⾝是否已经加载过当前class⽂件,如果加载过则直接返回,如果没有加载过,则委托他的⽗类(ExtClassLoader)尝试进⾏加载,ExtClassLoader也会先查看⾃⼰是否加载过,加载过则直接返回,没有加载过,则继续委派给BootStrapClassLoader,如果直⾄BootStrapClassLoader都没有加载过,则会AppClassLoader会尝试进⾏加载。
JVM工作原理

JVM工作原理JVM(Java虚拟机)是Java程序的运行环境,它负责将Java源代码编译成可执行的字节码,并提供运行时环境来执行字节码。
JVM的工作原理涉及到类加载、内存管理、垃圾回收、即时编译等多个方面。
1. 类加载JVM通过类加载器(ClassLoader)来加载Java类。
类加载器根据类的全限定名(包括包名和类名)在类路径中查找对应的字节码文件,并将其加载到内存中。
类加载器采用双亲委派模型,即先由父类加载器尝试加载类,如果父类加载器无法加载,则由子类加载器尝试加载。
这种模型保证了类的唯一性和安全性。
2. 内存管理JVM将内存分为多个区域,包括方法区、堆、栈和程序计数器。
方法区存储类的元数据信息,如字段、方法、常量池等。
堆是存放对象实例的区域,通过垃圾回收机制来管理内存的分配和释放。
栈用于存储方法的局部变量和方法调用信息。
程序计数器用于指示当前线程执行的字节码指令。
3. 垃圾回收JVM通过垃圾回收机制自动回收不再使用的对象内存。
垃圾回收器会定期扫描堆内存,标记所有还在使用的对象,然后清理掉未被标记的对象。
常见的垃圾回收算法有标记-清除、复制、标记-整理等。
JVM还提供了不同的垃圾回收器,如Serial、Parallel、CMS、G1等,可以根据应用场景选择合适的垃圾回收器。
4. 即时编译JVM使用即时编译器(Just-In-Time Compiler)将热点代码(经常被执行的代码)编译成本地机器码,以提高执行效率。
JVM会监测程序的运行情况,根据热点代码的执行频率和调用关系进行优化编译。
即时编译器可以选择不同的编译策略,如解释执行、编译执行或混合执行。
5. 内存模型JVM定义了Java程序在多线程环境下的内存模型,保证多线程的内存可见性和有序性。
内存模型规定了线程之间如何进行通信和同步。
JVM使用主内存和工作内存的概念,线程之间的共享变量存储在主内存中,每个线程有自己的工作内存,线程对共享变量的操作先在工作内存中进行,然后通过主内存来同步和通信。
JVM双亲委派模型及其优点

JVM双亲委派模型及其优点JVM双亲委派模型及其优点什么是双亲委派模型?双亲委派模型:如果⼀个类加载器收到了类加载请求,它并不会⾃⼰先去加载,⽽是把这个请求委托给⽗类的加载器去执⾏,如果⽗类加载器还存在其⽗类加载器,则进⼀步向上委托,依次递归,请求最终将到达顶层的启动类加载器,如果⽗类加载器可以完成类加载任务,就成功返回,倘若⽗类加载器⽆法完成此加载任务,⼦加载器才会尝试⾃⼰去加载,这就是双亲委派模式。
简单来说就是:⾸先从底向上检查类是否已经加载过如果都没有加载过的话,那么就⾃顶向下的尝试加载该类为什么使⽤双亲委派模型?1.避免字节码重复加载采⽤双亲委派模式的是好处是Java类随着它的类加载器⼀起具备了⼀种带有优先级的层次关系,通过这种层级关可以避免类的重复加载,当⽗亲已经加载了该类时,就没有必要⼦ClassLoader再加载⼀次。
2.程序更加安全,核⼼api不会被替换假设通过⽹络传递⼀个名为ng.Integer的类,通过双亲委托模式传递到启动类加载器,⽽启动类加载器在核⼼Java API发现这个名字的类,发现该类已被加载,并不会重新加载⽹络传递的过来的ng.Integer,⽽直接返回已加载过的Integer.class,这样便可以防⽌核⼼API库被随意篡改。
相同的class⽂件被不同的classloader加载就是不同的两个类。
双亲委派模型类加载器分类:1、启动类加载器(Bootstrap)C++写的2、扩展类加载器(Extension)JAVA3、应⽤程序类加载器(AppClassLoader)也叫做系统类加载器,加载当前应⽤的classpath下的所有类。
4、⽤户⾃定义加载器:ng.ClassLoader的⼦类,⽤户可以定制类的加载⽅式。
打印加载器的加载路径:import uncher;import sun.misc.URLClassPath;import .URL;import .URLClassLoader;public class Example {public static void main(String[] args) {//打印启动类加载器的加载路径URLClassPath bootstrapClassPath = Launcher.getBootstrapClassPath();for(URL url : bootstrapClassPath.getURLs()){System.out.println(url.getPath());}//打印扩展类加载器的加载路径URLClassLoader extClassLoader = (URLClassLoader)ClassLoader.getSystemClassLoader().getParent();for(URL url : extClassLoader.getURLs()) {System.out.println(url.getPath());}//打印应⽤程序类加载器加载路径URLClassLoader appClassLoader = (URLClassLoader)ClassLoader.getSystemClassLoader();for(URL url : appClassLoader.getURLs()) {System.out.println(url.getPath());}}}打印加载器实例对象:BootStrapClassLoader 是⼀个纯的C++实现,没有对应的Java类。
双亲委派机制的作用

双亲委派机制的作用双亲委派机制是一种用于解决类加载相关问题的机制,它广泛应用于Java等编程语言中。
双亲委派机制的作用是保证类的加载安全性、安全性和唯一性,同时减少重复加载,提高系统性能。
它的工作原理是当一个类被加载时,首先会委派给父类加载器,依次递归,直到顶层的父类加载器执行加载操作。
只有当父类加载器不能加载该类时,才会由子类加载器尝试加载该类。
以下是双亲委派机制的作用分析。
1. 类加载安全性:双亲委派机制通过逐级委派的方式,保证类的加载是由最上层的启动类加载器(Bootstrap ClassLoader)开始的。
这个类加载器是由Java虚拟机提供的,并且它是不可更改的。
因此,只有由Java虚拟机提供的类加载器加载的类,才能保证其安全性。
双亲委派机制可以防止恶意代码对系统进行破坏。
因为类加载器会首先委派给父类加载器,父类加载器再逐级向上委派给更高级的加载器,最终经过一系列的验证和校验后,才会加载该类。
这样可以确保加载的类是可信任的,不会引入不安全的代码。
2.类加载的唯一性:双亲委派机制可以确保同一个类只被加载一次,避免了多次加载造成的内存浪费。
在双亲委派机制中,如果一个类已经被一些类加载器加载了,那么其他类加载器就不会再重新加载该类。
相反,它们会直接使用已经加载的类。
这样可以避免出现多个版本的同一个类,保证类的唯一性和一致性。
3.类加载的重用性:双亲委派机制可以避免重复加载类,提高系统的性能。
在双亲委派机制中,如果一个类已经被加载过了,那么后续需要加载同一个类的请求就会直接使用已经加载的类。
这样可以避免对同一个类进行重复加载,减少了资源的浪费,提高了系统性能。
4.类加载的层次性:双亲委派机制通过定义多个不同层次的类加载器,实现了类加载的层次性管理。
在双亲委派机制中,每个类加载器都有一个父类加载器,并且可以根据需要自定义一个类加载器。
父类加载器通过委派给子类加载器的方式,实现了类加载的层次性管理。
打破双亲委派机制的三种情况

打破双亲委派机制的三种情况首先,来看第一种情况,即自定义类加载器。
在Java中,我们可以通过自定义类加载器来实现自己的类加载逻辑。
一般而言,我们自定义的类加载器会继承自ClassLoader类,并重写其中的findClass方法来实现自定义的类加载逻辑。
自定义类加载器打破了双亲委派机制,因为它可以在双亲委派机制的基础上添加额外的逻辑,例如,先尝试从特定目录或者远程服务器加载类,如果加载失败再委托给父类加载器。
这种情况下,自定义的类加载器可以破坏双亲委派机制,实现自己的类加载逻辑。
其次,是使用线程上下文类加载器。
在Java的线程模型中,每个线程都有一个上下文类加载器,用于在当前线程中加载类。
线程上下文类加载器可以通过Thread类的setContextClassLoader方法来设置,也可以通过Thread类的getContextClassLoader方法来获取。
在一些情况下,线程上下文类加载器可以用来打破双亲委派机制。
例如,在使用SPI (Service Provider Interface)框架时,线程上下文类加载器可以被设置为当前线程的类加载器,以便在加载SPI配置文件时,可以委托给当前线程的类加载器来加载。
最后,是破坏双亲委派机制。
双亲委派机制是Java类加载器的一种工作机制,在加载类时,优先委托给父类加载器,如果父类加载器无法加载,则由子类加载器进行加载。
这样的机制可以保证类的加载顺序和一致性。
然而,有些情况下,可能会有需要破坏这种机制的场景。
例如,在一些特定的应用中,可能希望更改一些类的行为,可以通过在类路径中放置一个与要加载的类同名的类,然后将这个类的路径优先级设为高于双亲委派机制中的父类加载器的路径,这样就可以破坏双亲委派机制,使得指定的类由这个更高优先级的类加载器来加载。
综上所述,打破双亲委派机制的三种情况可以是:自定义类加载器、使用线程上下文类加载器以及破坏双亲委派机制。
这些情况下,可以通过添加额外的逻辑或者更改类加载顺序来实现对类加载过程的自定义。
双亲委派机制

• 如果父类加载器无法加载(在其搜索范围中没有找到所需的类),子类加载器 才会尝试自己去加载。
• 这种逐级委派的方式确保了类加载的有序性和唯一性,避免了类的重复加载。
YOUR LOGO
20XX.XX.XX
双亲委派机制详解
Java虚拟机中的类加载安全机制
双亲
目录内容
• 双亲委派机制概述 • 双亲委派机制原理 • 双亲委派机制的优势 • 结束语
双亲委派机制概述
• Java虚拟机(JVM)中一种重要的类加载机制,它确保了类的加载过程的有序 性、唯一性和安全性。
双亲委派机制原理
双亲委派机制的优势
• 防止类的重复加载:当类加载器收到类加载的请求时,它会首先将这个请求委 派给它的父类加载器去完成。如果父类加载器能够找到这个类,就直接返回这 个类,无需子类加载器再次加载。这种机制确保了同一个类只会被加载一次, 避免了类的重复加载和内存浪费。
• 类型安全:双亲委派机制确保Java的核心类库(如ng、java.util等)始终 是由最顶层的启动类加载器(Bootstrap Class Loader)来加载的。由于启动类 加载器是由JVM实现的一部分,因此它加载的类被认为是可靠的、安全的。这 种机制防止了应用程序中的类加载器篡改核心类库中的类,从而确保了Java的 类型安全。
类加载过程
• 加载:类加载器根据类的全限定名定位并读取字节码文件,生成Class对象。 • 验证:对加载的字节码进行验证,确保字节码的正确性和安全性。 • 准备:为类的静态变量分配内存并设置默认初始值。 • 解析:将符号引用解析为直接引用。
双亲委派机制原理

双亲委派机制原理双亲委派机制的原理是:当一个类加载器(ClassLoader)接收到加载一个类的请求时,它首先检查自己是否已经加载了该类。
如果已经加载,那么直接返回该类;如果没有加载,则将加载请求委派给它的父类加载器(Parent ClassLoader),一直向上委派,直到委派到最顶层的类加载器(Bootstrap ClassLoader)。
这个机制之所以被称为双亲委派,是因为类加载器在接收到加载请求后,首先委派给父类加载器,如果父类加载器也没有加载该类,则再由子类加载器尝试加载。
这样的层层向上委派的过程就形成了一种“双亲”的关系。
这种双亲委派机制的好处是:1. 安全性:通过双亲委派机制,可以确保核心类库只能由Bootstrap ClassLoader加载,而用户自定义的类只能由自定义的ClassLoader加载。
这样就可以有效防止恶意类的加载和篡改。
2.避免类的重复加载:当一个类已经被加载后,它会被缓存起来,下次再有加载请求时,会直接返回缓存中的类,避免了重复加载。
这样可以节省内存空间,并提升系统性能。
3. 灵活性:由于可以使用自定义的ClassLoader加载类,因此可以根据需要实现不同的加载策略。
比如可以从网络、数据库等地方加载类,并且可以在运行时动态地加载和卸载类。
双亲委派机制的具体实现是通过类加载器的双亲指针(Parent Pointer)来实现的。
每个类加载器都有一个指向其父类加载器的引用,当一个类加载器收到加载请求后,会先检查自身是否已经加载了该类,如果没有加载则通过双亲指针将加载请求委派给父类加载器,直到最顶层的Bootstrap ClassLoader。
如果所有的父类加载器都无法找到该类,则该类加载器会尝试自己加载。
在JVM中,Bootstrap ClassLoader是最顶层的类加载器,它是由JVM自身实现的,并不是一个普通的Java类加载器。
Bootstrap ClassLoader负责加载JVM运行时环境需要的核心类库,如rt.jar中的类。
tomcat打破双亲委派机制

tomcat打破双亲委派机制tomcat在jvm提供的类加载器上进⾏了扩展,并且打破了双亲委托机制CommonClassLoader、CatalinaClassLoader、SharedClassLoader和WebappClassLoader则是Tomcat⾃⼰定义的类加载器,它们分别加载/common/*、/server/*、/shared/*(在tomcat 6之后已经合并到根⽬录下的lib⽬录下)和/WebApp/WEB-INF/*中的Java类库。
其中WebApp类加载器和Jsp类加载器通常会存在多个实例,每⼀个Web应⽤程序对应⼀个WebApp类加载器,每⼀个JSP⽂件对应⼀个Jsp类加载器。
commonLoader:Tomcat最基本的类加载器,加载路径中的class可以被Tomcat容器本⾝以及各个Webapp访问;catalinaLoader:Tomcat容器私有的类加载器,加载路径中的class对于Webapp不可见;sharedLoader:各个Webapp共享的类加载器,加载路径中的class对于所有Webapp可见,但是对于Tomcat容器不可见;WebappClassLoader:各个Webapp私有的类加载器,加载路径中的class只对当前Webapp可见;从图中的委派关系中可以看出:CommonClassLoader能加载的类都可以被Catalina ClassLoader和SharedClassLoader使⽤,从⽽实现了公有类库的共⽤,⽽CatalinaClassLoader和Shared ClassLoader⾃⼰能加载的类则与对⽅相互隔离。
WebAppClassLoader可以使⽤SharedClassLoader加载到的类,但各个WebAppClassLoader实例之间相互隔离。
⽽JasperLoader的加载范围仅仅是这个JSP⽂件所编译出来的那⼀个.Class⽂件,它出现的⽬的就是为了被丢弃:当Web容器检测到JSP⽂件被修改时,会替换掉⽬前的JasperLoader的实例,并通过再建⽴⼀个新的Jsp类加载器来实现JSP⽂件的HotSwap 功能。
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
JVM的双亲委派机制
JVM类加载器是什么机制?为什么使用这种机制(这种机制的好处是什么)?说下类加载流程?用代码验证类加载机制。
为什么要破坏类的这种加载机制?
我们已经知道了JVM类加载器的四种加载机制,那么这四种加载机制是怎么个加载过程呢?我们再来看看类加载器的图例:
图一:双亲委派机制图例
先来看看类和类加载器:
类和类加载器
对于任意一个类,都需要由加载它的类加载器和这个类本身一同确立其在Java虚拟机中的唯一性。
当我们比较两个类是否相等的时候,前提是:只有在这两个类是由同一个类加载器加载为前提下才有意义的。
通过上面文字描述,我们知道了,在JVM中,类可以通过不同类加载器加载的。
这个信息很重要。
既然类可以通过不同加载器加载后,使其不与其他类equals。
那么,是否可以自己写个ng.string类呢?我们都知道,自己写的类是appClassLoader加载到JVM的;jvm原生的string类是由bootstrapClassLoader加载的。
这两个类是不同的加载器加载,是不eqs的。
那么实际上可以吗?答案是:不可以(下文凯哥Java(wxID:kaigejava)会通过代码来证实)。
为什么不可以呢?因为双亲委派机制的缘故。
双亲委派机制
如果从JVM角度来讲的话,类的加载器只有两种:启动类加载器。
这个类是C++写的,是JVM虚拟机自身的一部分;另一种就是所有其他类的类加载器了。
是Java写的,独立于虚拟机外部的,而且都是继承于:ng.ClassLoader的。
从我们Java开发任意角度来看的话,就可以分为四种类加载器了。
这里先不具体概述了,在下文会介绍的。
在图一的图例中展示的类加载器之间层次管理,就被称之为双亲委派模型(Parents Delegation Model)。
双亲委派机制药圈,除了顶层的类加载器(Bootstrap)外,其余的类加载器都应该有自己的父类加载器。
PS:通过上一篇《JVM学习笔记之类装载器-ClassLoader》的最后,我们通过代码演示了,自定义类的父加载器是appClassLoader,appClassLoader的父加载器是扩展类加载器。
双亲委派机制的执行过程:
如果一个类加载器收到了类加载的请求,这个类加载器不会先尝试加载这个类,而是会先把这个请求委派给自己的父类加载器去完成,在每个层次的类加载器都是依此类推的。
因此所有的类加载器请求其最后都应该被委派到顶层的启动类加载器中(Bootstrap),只有当父类的加载器在自己管辖范围内(文末会介绍每个类加载器管理范围的)没有找到所需要的类的时候,子类加载器才会尝试自己去加载的,如果自己管理范围内也找不到需要加载的就会抛出:ClassNotFoundException这个异常了。
这就是为什么如果我们自己写ng.string类的时候,是不行的。
我们可以自己尝试着创建一个ng.String类,类里面只有一个main方法,执行会是什么样的呢?
如上图:运行的时候,报错了。
根据提示,我们就能知道,自己写的string类在启动的时候,类加载了,向上父类委派,最终委派到启动类加载器了,启动了加载器发现自己管辖的范围内存在String类,然后调用Main方法的时候,发现没有这个方法,就报错了。
这个流程执行的简图:
双亲委派流程示意图
图二:双亲委派机制流程图
双亲委派机制的好处
JVM为什么要使用双亲委派这种机制呢?有什么好处呢?通过我们自己写的ng.string这个类启动报错中,我们就可以得出以下双亲委派的优点:
1:保证了JVM提供的核心类不被篡改,保证class执行安全
比如上文的string类,无论哪个加载器要加载这个类的话,由于双亲委派机制,最终都会交由最顶层的启动类加载器来加载,这样保证了string类在各种类加载器环境中,都是同一个类。
试想下,没有双亲委派机制的话,各个加载器自己加载string类,有可能不同类加载器加载的string方法不一样,那样的话,我们的程序是不是就会一片混乱了。
2:防止重复加载同一个class
从图二:双亲委派机制流程图中,我们可以看出,委托向上问一问,如果加载过,就不用再加载了。
双亲委派机制简单理解
简单一句话:我爸是李刚,有事找我爸。
简单三个字:往上捅
双亲委派就是,有啥事,先问问老爹,如果老爹不行,再问问爷爷,如果爷爷也没有,再告爸爸,爸爸再告诉诉儿子,你自己看着办吧。
为什么要往上捅呢?是因为沙箱安全机制
四种类加载机制的管辖范围
一:启动类加载器(BootstrapClassLoader):
是由c++编写的,是JVM自身的一部分。
用来加载Java核心类库的(java.*)的。
构造ExtClassLoader和AppClassLoader的。
需要注意的是:由于其是虚拟机自身的一部分,开发者是服务直接获取到启动类加载器的引用的,所以是不允许直接通过引用进行操作。
这个类加载器负责存放在<JAVA_HOME>\lib目录中的,或是被-Xbootclasspath参数所指定的路径中的,并且是虚拟机识别的类库加载到虚拟机内存中
二:扩展类加载器(ExtensionClassLoader):
Java语言编写的,加载扩展库。
如classPath中的jre,javax.*(也即:<JAVA_HOME>\lib\ext目录中)或是java.ext.dir指定位置中的类。
开发者可以直接使用这个扩展类加载器
Java语言编写的,这个加载器是由uncher$ExtClassLoader来实现的
三:应用程序类加载器(Application ClassLoader)
这个类加载器是由uncher#AppClassLoader实现的。
由于这个类加载器是ClassLoader中的getSystemClassLoader()方法的返回值。
所以也称为系统加载器。
赋值加载用户类路径(ClassPath)上所指定的类库。
四:用户自定义类加载器(CustomClassLoader)
Java语言编写的,用户自定义类加载器,可以加载指定路径的class文件。