JVM -- 类的加载

发布时间:2026/7/26 9:52:23

JVM -- 类的加载 一.类的生命周期类的生命周期描述了一个类加载、使用、卸载的整个过程。整体可以分为加载 -- 验证 -- 准备 -- 解析 -- 初始化 -- 使用 -- 卸载1.1 加载阶段1、加载(Loading)阶段第一步是类加载器根据类的全限定名通过不同的渠道以二进制流的方式获取字节码信息程序员可以使用Java代码拓展的不同的渠道。从本地磁盘上获取文件运行时通过动态代理生成比如Spring框架Applet技术通过网络获取字节码文件2、类加载器在加载完类之后Java虚拟机会将字节码中的信息保存到方法区中方法区中生成一个InstanceKlass对象保存类的所有信息里边还包含实现特定功能比如多态的信息。3、Java虚拟机同时会在堆上生成与方法区中数据类似的java.lang.Class对象作用是在Java代码中去获取类的信息以及存储静态字段的数据JDK8及之后。1.2 验证1、文件格式验证比如文件是否以0xCAFEBABE开头主次版本号是否满足当前Java虚拟机版本要求。2、元信息验证例如类必须有父类super不能为空。3、验证程序执行指令的语义比如方法内的指令执行中跳转到不正确的位置。4、符号引用验证例如是否访问了其他类中private的方法等。1.3 准备准备阶段为静态变量static分配内存并设置初值每一种基本数据类型和引用数据类型都有其初值。数据类型初始化int0long0Lshort0char‘\u0000’byte0booleanfalsedouble0.0引用数据类型null注意final修饰的基本数据类型的静态变量准备阶段直接会将代码中的值进行赋值。1.4 解析解析阶段主要是将常量池中的符号引用替换为直接引用符号引用就是在字节码文件中使用编号来访问常量池中的内容。直接引用不在使用编号而是使用内存中地址进行访问具体的数据。1.3 初始化阶段初始化阶段会执行字节码文件中clinitclass init 类的初始化方法的字节码指令包含了静态代码块中的代码并为静态变量赋值。访问一个类的静态变量或者静态方法注意变量是final修饰的并且等号右边是常量不会触发初始化。当初始化一个类的时候如果发现其父类还没有进行过初始化则需要先触发其父类的初始化虚拟机会保证一个类的() 方法在多线程环境中被正确加锁和同步二.类加载器类加载器ClassLoader是Java虚拟机提供给应用程序去实现获取类和接口字节码数据的技术类加载器只参与加载过程中的字节码获取并加载到内存这一部分。类加载器会通过二进制流的方式获取到字节码文件的内容接下来将获取到的数据交给Java虚拟机虚拟机会在方法区和堆上生成对应的对象保存字节码信息。2.1 启动类加载器启动类加载器Bootstrap ClassLoader是由Hotspot虚拟机提供的、使用C编写的类加载器。默认加载Java安装目录**/jre/lib下的类文件**比如rt.jartools.jarresources.jar等。用户扩展基础jar包 如果用户想扩展一些比较基础的jar包让启动类加载器加载有两种途径放入jre/lib下进行扩展。不推荐尽可能不要去更改JDK安装目录中的内容会出现即时放进去由于文件名不匹配的问题也不会正常地被加载。使用参数进行扩展。推荐使用-Xbootclasspath/a:jar包目录/jar包名 进行扩展参数中的/a代表新增。2.2 扩展类加载器扩展类加载器Extension Class Loader是JDK中提供的、使用Java编写的类加载器。默认加载Java安装目录/jre/lib/ext下的类文件。通过扩展类加载器去加载用户jar包放入/jre/lib/ext下进行扩展。不推荐尽可能不要去更改JDK安装目录中的内容。使用参数进行扩展使用参数进行扩展。推荐使用-Djava.ext.dirsjar包目录 进行扩展,这种方式会覆盖掉原始目录可以用;(windows):(macos/linux)追加上原始目录2.3 应用程序类加载器应用程序类加载器会加载classpath下的类文件默认加载的是项目中的类以及通过maven引入的第三方jar包中的类。2.4 三者关系启动类加载器由C实现没有父类。拓展类加载器(ExtClassLoader)由Java语言实现父类加载器为null应用程序类加载器(AppClassLoader)由Java语言实现父类加载器为ExtClassLoader三.双亲委派机制双亲委派机制指的是当一个类加载器接收到加载类的任务时会自底向上查找是否加载过再由顶向下进行加载。双亲委派机制的作用1.保证类加载的安全性。通过双亲委派机制避免恶意代码替换JDK中的核心类库比如java.lang.String确保核心类库的完整性和安全性。2.避免重复加载。双亲委派机制可以避免同一个类被多次加载。3.1 打破双亲委派机制双亲委派机制默认的加载类逻辑是当一个类加载器需要加载某个类时先请求它的父类加载器进行加载只有在父加载器找不到该类时它才会尝试自己加载。为了打破这一机制通常需要自定义一个类加载器。并通过重写 ClassLoader 的 loadClass 方法完全接管类加载的流程可以实现自己的类加载逻辑。/** * 打破双亲委派机制 - 自定义类加载器 */publicclassBreakClassLoader1extendsClassLoader{privateStringbasePath;privatefinalstaticStringFILE_EXT.class;//设置加载目录publicvoidsetBasePath(StringbasePath){this.basePathbasePath;}//使用commons io 从指定目录下加载文件privatebyte[]loadClassData(Stringname){try{StringtempNamename.replaceAll(\\.,Matcher.quoteReplacement(File.separator));FileInputStreamfisnewFileInputStream(basePathtempNameFILE_EXT);try{returnIOUtils.toByteArray(fis);}finally{IOUtils.closeQuietly(fis);}}catch(Exceptione){System.out.println(自定义类加载器加载失败错误原因e.getMessage());returnnull;}}//重写loadClass方法OverridepublicClass?loadClass(Stringname)throwsClassNotFoundException{//如果是java包下还是走双亲委派机制if(name.startsWith(java.)){returnsuper.loadClass(name);}//从磁盘中指定目录下加载byte[]dataloadClassData(name);//调用虚拟机底层方法方法区和堆区创建对象returndefineClass(name,data,0,data.length);}默认情况下自定义类加载器的父类加载器是应用程序类加载器在同一个Java虚拟机中只有相同类加载器相同的类限定名才会被认为是同一个类所以两个自定义类加载器加载相同限定名的类不会冲突3.2 Tomcat的自定义类加载器common类加载器common类加载器主要加载tomcat自身使用以及应用使用的jar包默认配置在catalina.properties文件中。common.loader“catalina.base/lib,{catalina.base}/lib,catalina.base/lib,{catalina.base}/lib/*.jar”它是URLClassLoader类的子类对象根据URL获取jar包中的class字节码文件。catalina类加载器catalina类加载器主要加载tomcat自身使用的jar包不让应用使用默认配置在catalina.properties文件中。server.loader 默认配置为空为空时catalina加载器和common加载器是同一个。shared类加载器shared类加载器主要加载应用使用的jar包不让tomcat使用默认配置在catalina.properties文件中。shared.loader 默认配置为空为空时shared加载器和common加载器是同一个。ParallelWebappClassLoader类加载器ParallelWebappClassLoader类加载器可以多线程并行加载应用中使用到的类每个应用都拥有一个自己的该类加载器。ParallelWebappClassLoader的执行流程默认这里打破了双亲委派机制应用中的类如果没有加载过。会先从当前类加载器加载然后再交给父类加载器通过双亲委派机制加载。JasperLoader类加载器JasperLoader类加载器负责加载JSP文件编译出来的class字节码文件为了实现热部署不重启让修改的jsp生效每一个jsp文件都由一个独立的JasperLoader负责加载。

相关新闻