JVM类加载机制

JVM类加载机制

关于JVM的类加载机制,可以理解为:把编译完成的 .class文件,从文件硬盘加载到内存中(元数据区)这样的一个过程,最终要获取到类对象。这个过程,可以大致理解为以下几个步骤:

加载:把 .class 文件找到,打开文件,读文件,把文件内容读到内存中;

验证:检查 .class 文件格式对不对,.class文件是一个二进制文件,此处的检查是根据官方提供的 JVM 虚拟机规范,它会说明描述 .class 的规范;

准备:给类对象分配内存空间(在元数据区占用一个空间) ,内存初始化成全0,为类中定义的静态变量分配内存,并设置初始值也为0;

解析: 初始化字符串常量,把符号引用转成直接引用;

符号引用转成直接引用:对于字符串常量来说,是需要有一块内存空间,来存这个字符的实际内容的,还需要有一个引用,来保存这个内存空间的起始地址,而在类加载过程中,字符串常量是存储在 .class 文件中的,而不是内存上的,此时的这个 "引用" 记录的不是字符串常量的真正地址,而是它在文件中的一个偏移量,这个偏移量就理解为 "符号引用";

在类加载之后,才真正的把这个字符串常量放在内存中,此时才是真正拥有内存地址,此时的内存地址就理解为 "直接引用" ;

初始化: 针对类对象里面的内容进行初始化,加载父类,执行父类静态代码块,父类静态变量赋值,子类静态代码块,子类静态变量赋值,父类初始化模块,父类普通变量,父类构造器,子类初始化模块,子类普通变量,子类构造器...

一个类什么时候会被加载呢?

并不是Java程序一运行,就把所有的类都加载了,而是等到了真正用到的时候,才会去加载,属于是懒汉模式,一般会有以下三种情况:

  1. 构造类的实例;
  2. 调用了这个类的静态方法,或者使用到了这个类的静态属性;
  3. 加载子类的时候,通常会先加载其父类;
    一般是用到了才会进行类加载,并且加载过一次后,后续再使用也不需要进行重复加载了;
    双亲委派模型

双亲委派模型,描述的是JVM类加载机制中的 加载 过程,也就是找 .class 文件的过程;

JVM 默认提供了三个类加载器:

  1. BootstrapClassLoader:负责加载标准库中的类;
  2. ExtensionClassLoader:负责加载 JVM 扩展库中的类;
  3. ApplicationClassLoader: 负责加载用户提供的,也就是用户代码中的类;
    在这三个类加载器中,存在一个 "父子关系" --- 每个 ClassLoader 类中会有一个 parent 属性来标识自己的父加载器:
    ApplicationClassLoader 的 parent 是 ExtensionClassLoader;
    ExtensionClassLoader 的 parent 是 BootstrapClassLoader;
    类加载器的加载过程

当加载一个类的时候,是从 ApplicationClassLoader 开始的,但它会把当前的加载任务交给父亲去做,也就是让 ExtensionClassLoader 去加载,然后 ExtensionClassLoader 也把任务交给父加载器,也就是 BootstrapClassLoader 去加载,此时 BootstrapClassLoader 的 parent 是 null 了,也就只能自己去加载,此时 BootstrapClassLoader 就要搜索自己负责的标准库中的类,如果能找到,就加载,如果找不到,就再返回交给 ExtensionClassLoader 去加载,同样的道理,如果 ExtensionClassLoader 在自己负责的 JVM 扩展库中找到了对应的类,就加载,如果找不到,就再返回给 ApplicationClassLoader 去加载,如果找到了就加载,如果到最后都没有找到,那么就会抛出 类找不到 这样的一个异常;

就大致是一个这样的执行过程:

之所以以这样的一个加载顺序,让标准库先加载,然后再扩展库,最后再是用户写的类,是为了防止用户可能会写出一些标准库或者扩展库已经存在的类的,就比如 java.lang.String,而这个是标准库中已经存在的类的,按照上述的加载顺序,JVM 最后加载出来的就是标准库中的类,而不是用户自己写的;

另一方面,类加载器也是可以自定义的,以上三个是 JVM 自带的,用户自定义的类加载器,也可以加入到上述的流程中使用,自己写的类加载器,可以遵守双亲委派模型,也可以选择不遵守,例如 Tomcat,去加载 webapp 这里就是单独的类加载器,不遵守双亲委派模型;

相关推荐
大数据编程之光18 分钟前
Flink Standalone集群模式安装部署全攻略
java·大数据·开发语言·面试·flink
爪哇学长32 分钟前
双指针算法详解:原理、应用场景及代码示例
java·数据结构·算法
风尚云网36 分钟前
风尚云网前端学习:一个简易前端新手友好的HTML5页面布局与样式设计
前端·css·学习·html·html5·风尚云网
ExiFengs36 分钟前
实际项目Java1.8流处理, Optional常见用法
java·开发语言·spring
paj12345678937 分钟前
JDK1.8新增特性
java·开发语言
捂月43 分钟前
Spring Boot 深度解析:快速构建高效、现代化的 Web 应用程序
前端·spring boot·后端
繁依Fanyi1 小时前
简易安卓句分器实现
java·服务器·开发语言·算法·eclipse
慧都小妮子1 小时前
Spire.PDF for .NET【页面设置】演示:打开 PDF 时自动显示书签或缩略图
java·pdf·.net
m51271 小时前
LinuxC语言
java·服务器·前端
IU宝1 小时前
C/C++内存管理
java·c语言·c++