内容简介:类的唯一性:详情请访问类加载 - 类初始化新建数组
- 类(字节流)
- 接口(字节流)
- 数组类(由JVM直接生成)
- 泛型参数(类型擦除,伪泛型)
类加载过程
加载
- 加载: 查找字节流,并且据此创建类的过程
-
对于 数组类
,没有对应的字节流,而是由JVM直接生成的
- 对于其他类而言,JVM需要借助 类加载器 来完成查找字节流的过程
类加载器
- 启动类加载器(boot class loader):由C++实现,没有对应的 Java 对象,在Java中只能用null来指代
-
除了启动类加载器外,其他的类加载器都是 java.lang.ClassLoader
的子类,有对应的Java对象
- 这些类加载器需要先由另一个类加载器(如启动类加载器),加载至Java虚拟机中,方能执行类加载
-
双亲委派模型
- 每当一个类加载器接收到加载请求时,会先将请求转发给父类加载器
- 在父类加载器没有找到所请求的类的情况下,该类加载器才会尝试自己加载
-
Before Java9
- 启动类加载器(boot class loader):负责加载 最基础、最重要 的类(-Xbootclasspath)
- 扩展类加载器(extension class loader):父类加载器为 启动类加载器 ,负责加载 相对次要、但又通用 的类(java.ext.dirs)
-
应用类加载器(application class loader):父类加载器为 扩展类加载器
,负责加载应用程序路径下的类
- -cp
- -classpath
- 系统变量java.class.path
- 环境变量CLASSPATH
-
Since Java9
- 引入模块系统
- 扩展类加载器改名为 平台类加载器 (platform class loader)
- Java SE中除了少数几个关键模块(java.base)是由启动类加载器加载之外,其他的模块均由平台类加载器所加载
-
自定义类加载
- 例如对class文件进行加密,加载时再利用自定义的类加载器对其解密
命名空间+唯一性
类的唯一性: 类加载实例 + 类的全名
链接
- 链接:将创建的类 合并 至JVM,使之 能够执行 的过程
- 链接过程: 验证 、 准备 和 解析
验证
- 确保被加载到类能够 满足JVM的约束条件
- Java编译器生成的类文件必然满足JVM的约束条件
准备
- 为被加载类的静态字段分配内存
- Java代码中对静态字段的 具体初始化 ,会在 初始化阶段 进行
解析(可选)
- 解析:将 符号引用 解析成为 实际引用
-
在class文件被加载至JVM之前,这个类是无法知道其他类及其方法、字段所对应的具体地址,甚至不知道自己方法、字段的地址
- 因此每当需要引用这些成员时, Java编译器 都会生成一个 符号引用
- 在 运行阶段 ,这个符号引用一般都能够无歧义地定位到 具体目标 上
- 如果 符号引用 指向一个 未被加载的类 或者 未被加载到类的字段或方法 ,那么解析将会 触发这个类的加载 (未必触发这个类的链接和初始化)
-
Java虚拟机规范 并没有要求在链接过程中要完成解析
- 如果某些字节码使用了符号引用,那么在执行这些字节码之前,需要完成对这些符号引用的解析
初始化
-
初始化: 为标记为常量值的字段赋值
+
执行
方法 - 如果要初始化一个静态字段,可以在声明时直接赋值,也可以在静态代码块中对其赋值
- 如果 直接赋值 的静态字段被 final 所修饰,并且它的类型是 基本类型 或 字符串 时,那么该字段便会被 Java编译器 标记为 常量值 (ConstantValue), 其初始化直接由JVM完成
- 除此之外的直接赋值操作以及所有静态代码块中的代码,则会被Java编译器置于同一方法中,既 <clinit>
- JVM会通过 加锁 来保证类的 <clinit> 仅被执行一次
- 只有当初始化完成后,类才正式开始成为可执行的状态
触发时机
- new指令
- putstatic/getstatic指令
- invokestatic指令
-
反射
- Class.forName
- Class.newInstance
- 子类的初始化会触发父类的初始化
- 执行主类
- 如果接口定义了default方法,那么直接实现或者间接实现该接口的类进行初始化,会触发该接口初始化
详情请访问类加载 - 类初始化
// JVM参数:-verbose:class public class Singleton { private Singleton() { } private static class LazyHolder { static final Singleton INSTANCE = new Singleton(); static { System.out.println("LazyHolder.<clinit>"); } } private static Object getInstance(boolean flag) { if (flag) { // Loaded xxx.Singleton$LazyHolder from file:XXX // 新建数组知会导致加载,但不会导致初始化 return new LazyHolder[2]; } // LazyHolder.<clinit> // getstatic指令触发类的初始化 return LazyHolder.INSTANCE; } public static void main(String[] args) { getInstance(true); System.out.println("----"); getInstance(false); } }
新建数组
新建数组 只会触发加载阶段 ,而 不会触发链接和初始化阶段
$ java -cp ./asmtools.jar org.openjdk.asmtools.jdis.Main Singleton\$LazyHolder.class > Singleton\$LazyHolder.jasm.bak # 将字节码修改为不符合JVM规范,在类加载-链接阶段会报错(从而验证有没有执行到链接阶段) $ awk 'NR==1,/stack 1/{sub(/stack 1/, "stack 0")} 1' Singleton\$LazyHolder.jasm.bak > Singleton\$LazyHolder.jasm $ java -cp ./asmtools.jar org.openjdk.asmtools.jasm.Main Singleton\$LazyHolder.jasm $ java -verbose:class Singleton [Loaded Singleton$LazyHolder from file:/Users/zhongmingmao/Downloads/asmtools-7.0-build/dist/asmtools-7.0/lib/] ---- [Loaded java.lang.VerifyError from /Users/zhongmingmao/.sdkman/candidates/java/8.0.181-oracle/jre/lib/rt.jar] Exception in thread "main" [Loaded java.lang.Throwable$PrintStreamOrWriter from /Users/zhongmingmao/.sdkman/candidates/java/8.0.181-oracle/jre/lib/rt.jar] [Loaded java.lang.Throwable$WrappedPrintStream from /Users/zhongmingmao/.sdkman/candidates/java/8.0.181-oracle/jre/lib/rt.jar] [Loaded java.util.IdentityHashMap from /Users/zhongmingmao/.sdkman/candidates/java/8.0.181-oracle/jre/lib/rt.jar] [Loaded java.util.IdentityHashMap$KeySet from /Users/zhongmingmao/.sdkman/candidates/java/8.0.181-oracle/jre/lib/rt.jar] java.lang.VerifyError: Operand stack overflow Exception Details: Location: Singleton$LazyHolder.<init>()V @0: aload_0 Reason: Exceeded max stack size. Current Frame: bci: @0 flags: { flagThisUninit } locals: { uninitializedThis } stack: { } Bytecode: 0x0000000: 2ab7 0007 b1 at Singleton.getInstance(Singleton.java:22) at Singleton.main(Singleton.java:28)
转载请注明出处:http://zhongmingmao.me/2018/12/16/jvm-basic-load-class-md/
访问原文「类加载」获取最佳阅读体验并参与讨论
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:- 介绍同步加载、异步加载、延迟加载[原创]
- .net加载失败的程序集重新加载
- 虚拟机类加载机制:类加载时机
- 探秘类加载器和类加载机制
- hibernate中加载策略+批加载+懒加载异常【原创】
- [译] React 16.6 懒加载(与预加载)组件
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
信息学奥林匹克教程·提高篇
吴耀斌 / 湖南师范大学出版社 / 2003-1 / 24.00元
《信息学奥林匹克教程》(提高篇)既有各个算法设计基本思路的讲解及对求解问题的分析,注重了算法引导分析与不同算法的比较,又给出了具体的编程思路与参考程序,程序采用信息学竞赛流行的Turbo Pascal7.0语言编写,并注重结构化与可读性。一起来看看 《信息学奥林匹克教程·提高篇》 这本书的介绍吧!