JavaWeb技术内幕六:深入分析classloader工作机制

本文涉及的产品
全局流量管理 GTM,标准版 1个月
云解析 DNS,旗舰版 1个月
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
简介: 版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/a724888/article/details/81456439 这位大侠,这是我的公众号:程序员江湖。
版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/a724888/article/details/81456439


classloader顾名思义就是类加载器,负责将class加载到jvm中。

事实上,classloader除了能将class加载到jvm中,还有一个重要的作用就是会审查每个类应该由谁来加载,它是一种父优先的等级加载机。

classloader除了上述两个作用以外还有一个作用就是将class字节码重新解析成jvm统一要求的对象格式(说明class文件并不是加载进来就能用)

classloader类结构分析

classloader主要有几个方法

1 defineclass
defineclass主要负责将byte字节流解析成JVM能够识别的Class对象。比如通过网络获取字节流,然后转化为Class对象。

defineclass生成的Class对象还么有resolve,只会对象真正实例化时才会resolve(resolve是解析连接的过程,经过这一步Class对象才能真正被访问到)

2 findclass
defineclass和findclass方法通常是一起使用的,我们通过直接覆盖classloader父类的findclass方法来实现家在规则。

如果想在类加载到JVM时就被连接,那么亦可以在findclass中再次调用resolveclass方法来进行连接。

3 loadclass
loadclass按照双亲委派模型进行加载,加载不到才会调用findclass。
如果我们要在运行时加载一个类,直接调用this.getClass().getClassLoader().loadClass()方法就可以完成类加载了。

classloader是个抽象类,如果要实现自己的classloader,一般可以继承uriclassloader,因为这个类已经帮我们实现了大部分工作。

classloader的等级加载机制

整个jvm平台提供三层classloader

1 bootstrapclassloader
这个classloader只服务于JVM自身,主要加载JVM自身工作所需要的类,是JVM自己控制的,用户访问不到,这个类加载器也不遵守双亲委派模型,没有子类。

负责加载lib目录下面的类

2 extclassloader
extclassloader虽然是jvm的一部分,但是不是jvm实现的,它负责加载lib/ext下面的类

3 appclassloader专门为用户服务,父类是extclassloader。服务于classpath下的类加载。

如果我们要实现自己的类加载,最终都要把appclassloader作为父加载器。

事实上,bootstrapclassloader不是ext的父加载器,他也没有子类。

jvm加载class文件的两种方式:

1 隐式加载
是指jvm自动加载类的方式,不用显示调用classloader加载

2 显示加载
在代码中使用classloader加载。

如何加载class文件

classloader加载一个class文件到JVM时经过的过程

class文件 ——> findclass ——> class规范验证,准备,解析 ——> 类属性初始化赋值 ——> class对象

1 第一个阶段是把class文件把这个文件包含的字节码加载到内存

2 第二个阶段又可以分为三个步骤,分别是字节码验证、class类数据结构分析以及相应的内存分配和最后的符号表的链接。

3 第三个阶段是类中静态属性和初始化赋值,以及静态块的执行等。

加载字节码到内存(执行defineclass)

其实在抽象类classloader中并没有定义如何去加载

1 如何去找到指定类并且把它的字节码加载内存需要的子类中去实现,也就是要实现findclass方法。

2 我们来看一下子类urlclassloader是如何实现findclass的,在urlclassloader中通过一个urlclasspath帮助取的要加载的class文件字节流,而这个urlclasspath定义了到哪里去找这个class文件。

3 如果找到了这个class文件,再读取它的byte字节流,通过defineclass方法来创建类对象。

实际上,ext把默认的搜索路径指定为环境变量配置的lib/ext了。而appclassloader则指向classpath的环境变量。

验证与解析(执行verify,prepare和resolve)

1 字节码验证,类装入器对于类的字节码要做许多检测,以保证格式正确,行为正确。

2 类准备,在这个阶段准备代表每个类中定义的字段,方法和实现接口所必须的数据结构。

3 解析,在这个阶段装入器装入类所引用的其他所有类,可以用许多方式引用类,比如超类,接口,字段等。

初始化class对象(初始化类中的值)

在类中包含的静态初始化器都被执行,静态字段也被初始化为默认值。

常见加载类错误分析

classnotfoundexception

这个异常主要是因为找不到对应类的class字节码,说明classpath下没有指定文件存在。

noclassdefounderror

这个异常在用java命令行执行java类时可能会用到,因为指定类名时如果没写完整,就会出现这个问题,

unsatisfiedlinkerror

少见

classcastexception

类型转换失败,可能出现在不能转换的类型发生转换时。

exceptioninitializeerror

少见

常用的classloader分析

web应用中的一个servlet是如何加载类的,我们看一下它的classloader,发现层级关系是extclassloader -> appclassloader -> standardclassloader -> webappclassloader

tomcat在何时创建这些classloader:

1 standardclassloader在bootstrap类(tomcat的一个类)的initclassloaders方法中创建的。

2 standardclassloader创建成功时,会被设置为整个tomcat的根classloader

3 tomcat使用standardclassloader加载catalina并创建对象(catalina是tomcat的默认容器,catilina是tomcat以前的名字),整个tomcat容器的类加载器也是standardclassloader。

4 standardclassloader实际上并不处理加载逻辑,因为它是是appclassloader的代理,实际上的加载器还是appclassloader。

5 tomcat容器本身(catilina)是谁加载的不重要,我们要了解其中的servlet是谁加载的。

6 web.xml配置了每个servlet对应的类,所以应该是由类加载器显示加载的。(解析xml显示加载)

7 standardcontext(解析web.xml后映射的上下文实例)检查classloader是否存在,然后instancemanager这个类会去真正加载这些servlet实例。

8 instancemanager类使用的classloader是webappclassloader,它的处理和appclassloader有所不同。

1 首先检查webappclassloader是否已加载

2 如果没有,则检查在jvm中是否已加载,调用classloader的findloadedclass

3 如果前两个缓存没有,则用appclassloader加载

4 检查类是否在特殊的包名下,如果在的话可以用standardclassloader来加载

5 如果还没找到,则由webappclassloader来加载,回去web-inf/classes目录下查找,然后用defineclass方法生成Class对象。

如何实现自己的classloader

自定义classloader的使用场景

1 到自定义路径下查找指定class文件

2 获得网络传输的类的字节码,可能已加密

3 如果一个已加载的class文件被修改,可以重新加载这个类,实现热部署

加载自定义路径下的class文件

指定classpath即可

加载自定义格式的class文件

加密的class字节流,先解密再加载即可

实现类的热部署

jvm在加载类之前会判断请求的类是否已被加载,也就是通过findcloadedclass来判断。

1 看这个类的完整类名是否一致。

2 看加载这个类的类加载器是否是同一个实例。即使是同一个classloader类的两个实例,加载同一个类也会不一样。

3 所以实现热部署可以用classloader不同的实例对象来加载

如果重复地加载一个类,会抛出linkageerror错误。

Java应不应该加载动态类

Java有一个痛处,就是修改一个类,必须要重启一遍,才会重新加载,非常费时,如果使用动态的类加载就可以节省很多时间。

但是这是不好的,Java的优势正是基于共享对象的机制,达到信息的高度共享,对象一旦被创建就可以被重复利用。

如果动态加载一个对象到jvm,很难在jvm中平滑过渡。在理论上可以替换原对象然后修改所有指向该对象的引用,但是它违反了jvm的设计原则,。

对象的引用关系只有对象的创建者和持有和使用,jvm不可以干预对象的引用关系。

特例

对象不能被动态替换的原因是有很多引用指向它,很多变量保存着它的状态,如果对象不会被引用所指,没有人持有对象的状态,则可以动态地替换对象。

JSP就是这么做的,一旦JSP页面被修改,我们就可以热加载对应生成的servlet实例,这个实例是单例的,并且是无状态的,没有引用指向它,利用这个原理,很多热部署的方案也出现了。


微信公众号【Java技术江湖】一位阿里 Java 工程师的技术小站。(关注公众号后回复”Java“即可领取 Java基础、进阶、项目和架构师等免费学习资料,更有数据库、分布式、微服务等热门技术学习视频,内容丰富,兼顾原理和实践,另外也将赠送作者原创的Java学习指南、Java程序员面试指南等干货资源)

wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==

相关文章
|
24天前
|
缓存 JavaScript Java
常见java OOM异常分析排查思路分析
Java虚拟机(JVM)遇到内存不足时会抛出OutOfMemoryError(OOM)异常。常见OOM情况包括:1) **Java堆空间不足**:大量对象未被及时回收或内存泄漏;2) **线程栈空间不足**:递归过深或大量线程创建;3) **方法区溢出**:类信息过多,如CGLib代理类生成过多;4) **本机内存不足**:JNI调用消耗大量内存;5) **GC造成的内存不足**:频繁GC但效果不佳。解决方法包括调整JVM参数(如-Xmx、-Xss)、优化代码及使用高效垃圾回收器。
95 15
常见java OOM异常分析排查思路分析
|
10天前
|
Java
死磕-java并发编程技术(二)
死磕-java并发编程技术(二)
|
10天前
|
存储 Java 调度
死磕-java并发编程技术(一)
死磕-java并发编程技术(一)
|
21天前
|
设计模式 Java 关系型数据库
【Java笔记+踩坑汇总】Java基础+JavaWeb+SSM+SpringBoot+SpringCloud+瑞吉外卖/谷粒商城/学成在线+设计模式+面试题汇总+性能调优/架构设计+源码解析
本文是“Java学习路线”专栏的导航文章,目标是为Java初学者和初中高级工程师提供一套完整的Java学习路线。
176 37
|
21天前
|
缓存 前端开发 Java
【Java面试题汇总】Spring,SpringBoot,SpringMVC,Mybatis,JavaWeb篇(2023版)
Soring Boot的起步依赖、启动流程、自动装配、常用的注解、Spring MVC的执行流程、对MVC的理解、RestFull风格、为什么service层要写接口、MyBatis的缓存机制、$和#有什么区别、resultType和resultMap区别、cookie和session的区别是什么?session的工作原理
【Java面试题汇总】Spring,SpringBoot,SpringMVC,Mybatis,JavaWeb篇(2023版)
|
7天前
|
传感器 监控 数据可视化
【Java】智慧工地解决方案源码和所需关键技术
智慧工地解决方案是一种新的工程全生命周期管理理念。它通过使用各种传感器、数传终端等物联网手段获取工程施工过程信息,并上传到云平台,以保障数据安全。
30 7
|
12天前
|
Java
JAVA并发编程系列(9)CyclicBarrier循环屏障原理分析
本文介绍了拼多多面试中的模拟拼团问题,通过使用 `CyclicBarrier` 实现了多人拼团成功后提交订单并支付的功能。与之前的 `CountDownLatch` 方法不同,`CyclicBarrier` 能够确保所有线程到达屏障点后继续执行,并且屏障可重复使用。文章详细解析了 `CyclicBarrier` 的核心原理及使用方法,并通过代码示例展示了其工作流程。最后,文章还提供了 `CyclicBarrier` 的源码分析,帮助读者深入理解其实现机制。
|
5天前
|
安全 Java Android开发
JavaWeb解压缩漏洞之ZipSlip与Zip炸弹
JavaWeb解压缩漏洞之ZipSlip与Zip炸弹
22 2
|
12天前
|
缓存 负载均衡 Dubbo
Dubbo技术深度解析及其在Java中的实战应用
Dubbo是一款由阿里巴巴开源的高性能、轻量级的Java分布式服务框架,它致力于提供高性能和透明化的RPC远程服务调用方案,以及SOA服务治理方案。
39 6
|
23天前
|
存储 负载均衡 Java
Jetty技术深度解析及其在Java中的实战应用
【9月更文挑战第3天】Jetty,作为一款开源的、轻量级、高性能的Java Web服务器和Servlet容器,自1995年问世以来,凭借其卓越的性能、灵活的配置和丰富的扩展功能,在Java Web应用开发中占据了举足轻重的地位。本文将详细介绍Jetty的背景、核心功能点以及在Java中的实战应用,帮助开发者更好地理解和利用Jetty构建高效、可靠的Web服务。
32 2
下一篇
无影云桌面