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

本文涉及的产品
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
云解析 DNS,旗舰版 1个月
全局流量管理 GTM,标准版 1个月
简介: 版权声明:本文为博主原创文章,未经博主允许不得转载。 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程序员面试指南等干货资源)

相关文章
|
12天前
|
XML Java 编译器
Java注解的底层源码剖析与技术认识
Java注解(Annotation)是Java 5引入的一种新特性,它提供了一种在代码中添加元数据(Metadata)的方式。注解本身并不是代码的一部分,它们不会直接影响代码的执行,但可以在编译、类加载和运行时被读取和处理。注解为开发者提供了一种以非侵入性的方式为代码提供额外信息的手段,这些信息可以用于生成文档、编译时检查、运行时处理等。
45 7
|
2天前
|
存储 监控 安全
单位网络监控软件:Java 技术驱动的高效网络监管体系构建
在数字化办公时代,构建基于Java技术的单位网络监控软件至关重要。该软件能精准监管单位网络活动,保障信息安全,提升工作效率。通过网络流量监测、访问控制及连接状态监控等模块,实现高效网络监管,确保网络稳定、安全、高效运行。
27 11
|
12天前
|
JavaScript 安全 Java
java版药品不良反应智能监测系统源码,采用SpringBoot、Vue、MySQL技术开发
基于B/S架构,采用Java、SpringBoot、Vue、MySQL等技术自主研发的ADR智能监测系统,适用于三甲医院,支持二次开发。该系统能自动监测全院患者药物不良反应,通过移动端和PC端实时反馈,提升用药安全。系统涵盖规则管理、监测报告、系统管理三大模块,确保精准、高效地处理ADR事件。
|
17天前
|
监控 算法 Java
jvm-48-java 变更导致压测应用性能下降,如何分析定位原因?
【11月更文挑战第17天】当JVM相关变更导致压测应用性能下降时,可通过检查变更内容(如JVM参数、Java版本、代码变更)、收集性能监控数据(使用JVM监控工具、应用性能监控工具、系统资源监控)、分析垃圾回收情况(GC日志分析、内存泄漏检查)、分析线程和锁(线程状态分析、锁竞争分析)及分析代码执行路径(使用代码性能分析工具、代码审查)等步骤来定位和解决问题。
|
29天前
|
监控 前端开发 Java
【技术开发】接口管理平台要用什么技术栈?推荐:Java+Vue3+Docker+MySQL
该文档介绍了基于Java后端和Vue3前端构建的管理系统的技术栈及功能模块,涵盖管理后台的访问、登录、首页概览、API接口管理、接口权限设置、接口监控、计费管理、账号管理、应用管理、数据库配置、站点配置及管理员个人设置等内容,并提供了访问地址及操作指南。
|
1月前
|
JSON 前端开发 JavaScript
java-ajax技术详解!!!
本文介绍了Ajax技术及其工作原理,包括其核心XMLHttpRequest对象的属性和方法。Ajax通过异步通信技术,实现在不重新加载整个页面的情况下更新部分网页内容。文章还详细描述了使用原生JavaScript实现Ajax的基本步骤,以及利用jQuery简化Ajax操作的方法。最后,介绍了JSON作为轻量级数据交换格式在Ajax应用中的使用,包括Java中JSON与对象的相互转换。
45 1
|
1月前
|
SQL Java 数据库连接
在Java应用中,数据库访问常成为性能瓶颈。连接池技术通过预建立并复用数据库连接,有效减少连接开销,提升访问效率
在Java应用中,数据库访问常成为性能瓶颈。连接池技术通过预建立并复用数据库连接,有效减少连接开销,提升访问效率。本文介绍了连接池的工作原理、优势及实现方法,并提供了HikariCP的示例代码。
50 3
|
1月前
|
存储 Java 关系型数据库
在Java开发中,数据库连接是应用与数据交互的关键环节。本文通过案例分析,深入探讨Java连接池的原理与最佳实践
在Java开发中,数据库连接是应用与数据交互的关键环节。本文通过案例分析,深入探讨Java连接池的原理与最佳实践,包括连接创建、分配、复用和释放等操作,并通过电商应用实例展示了如何选择合适的连接池库(如HikariCP)和配置参数,实现高效、稳定的数据库连接管理。
61 2
|
1月前
|
SQL 监控 Java
Java连接池技术的最新发展,包括高性能与低延迟、智能化管理与监控、扩展性与兼容性等方面
本文探讨了Java连接池技术的最新发展,包括高性能与低延迟、智能化管理与监控、扩展性与兼容性等方面。同时,结合最佳实践,介绍了如何选择合适的连接池库、合理配置参数、使用监控工具及优化数据库操作,以实现高效稳定的数据库访问。示例代码展示了如何使用HikariCP连接池。
18 2
|
1月前
|
Java 数据库连接 数据库
深入探讨Java连接池技术如何通过复用数据库连接、减少连接建立和断开的开销,从而显著提升系统性能
在Java应用开发中,数据库操作常成为性能瓶颈。本文通过问题解答形式,深入探讨Java连接池技术如何通过复用数据库连接、减少连接建立和断开的开销,从而显著提升系统性能。文章介绍了连接池的优势、选择和使用方法,以及优化配置的技巧。
34 1