Java类加载器

一、加载步骤

Java代码通过编译成字节码,然后通过Java类加载器将字节码加载到JVM中运行,整体分成加载(Loading)、链接(Linking)、初始化(Initialization)三大步骤,其中链接有可以分成校验(Verification)、准备(Preparation)、解析(Resolution)三个小步骤,整体如下所示:
加载步骤

  • 加载 根据class完全限定名,获取二进制的classfile格式的字节流
  • 校验 检查class文件的字节流信息是否合法
  • 准备 创建静态字段,并将其初始化为表中默认字段,并分配方法表,即在方法区中分配这些变量所使用的的内存空间
  • 解析 解析常量池,主要是类或接口的解析、字段的解析、类方法解析、接口方法解析
  • 初始化 在类的首次主动使用时才执行类的初始化,依次执行父类初始化、类构造器、static静态变量赋值语句、static静态代码块
  • 使用 代码中正常使用
  • 卸载 Java8之后,可以开启类的卸载:-XX:+CMSClassUnloadingEnabled

二、加载时机

JVM规范列出了如下触发类的初始化时机:

  • 当虚拟机启动时,初始化用户指定的主类,就是启动执行的main方法所在的类
  • 当遇到用以新建目标类实例的new指令时,初始化new指令的目标类,就是new一个类的时候要初始化
  • 当遇到调用静态方法的指令时,初始化该静态方法中的类
  • 当遇到访问静态字段的指令时,初始化该静态字段的类
  • 子类的初始化会触发父类的初始化
  • 如果一个接口定义了default方法,那么直接实现或者间接实现该接口的类的初始化,会触发该接口的初始化
  • 使用反射API对某个类进行反射调用时,初始化这个类
  • 当初次调用MethodHandle实例时,初始化该MethodHandle指向的方法所在的类

但是以下的情况不会触发类的初始化,可能会触发类的加载:

  • 通过子类引用父类的静态字段,只会触发父类的初始化,而不会触发子类的初始化
  • 定义对象数组,不会触发该类的初始化
  • 常量在编译期间会存入调用类的常量池中,本质上并没有直接引用定义常量的类,不会触发定义常量所在的类
  • 常量在编译期间会存入调用类的常量池中,本质上并没有直接引用定义常量的类,不会触发定义常量所在的类
  • 通过Class.forName加载指定类时,如果指定参数initialize为false时,也不会触发类初始化,其实这个参数是告诉虚拟机,是否要对类进行初始化,Class.forName(“jvm.Hello”)默认会加载Hello类
  • 通过ClassLoader默认的loadClass方法,也不会触发初始化动作(加载了,但是不初始化)

三、加载机制

Java字节码文件的加载过程是类加载器(ClassLoader)来完成,系统自带的类加载器如下:

  • 启动类加载器(BootstrapClassLoader)
    加载Java核心类,用原生C++代码来实现,负责加载JDK中jre/lib/rt.jar里所有的class
  • 扩展类加载器(ExtClassLoader)
    负责加载JRE的拓展目录,lib/ext或者有java.ext.dirs系统属性指定
  • 应用类加载器(AppClassLoader)
    在没有使用自定义类加载器情况下,用户自定义类都由此加载器加载。JVM启动时加载来自Java命令的­classpath或者­cp选项、java.class.path系统属性指定的jar包和类路径。
    加载器结构
    类加载机制有如下特点:
  • 双亲委托 自定义加载器加载一个类时,先委托自己的父类加载器加载,一直委托到启动类加载器,再自己尝试加载
  • 负责依赖 如果一个加载器在加载某个类的时候,发现这个类依赖于另外几个类或者接口,也会尝试加载这些依赖项
  • 缓存加载 某个类被一个加载器加载后,会缓存加载结果
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值