分享一些关于 Java 虚拟机的一些基础知识,这些知识也通常是大厂经常面试到的内容,java 从业者是很有必要了解 java 虚拟机,这对更好的理解 java 很有帮助,这是 java 的内功。
关于java 虚拟机的学习资料推荐:
宋红康老师的 B 站视频:JVM 从入门到精通学习, 关于我记录的一些学习笔记也多从该视频吸收获取,强烈推荐。
《深入理解 java 虚拟机》这本书豆瓣评分 9.0 ,人气很高,很值得一看,推荐。
初识 JVM
java 不一定是最好的编程语言, java 虚拟机一定是最强大的虚拟机。
JVM 可以解释各种语言生成的字节码。
-
jvm 的位置
-
jvm 的整体架构
-
如果自己手写一个 Java 虚拟机的话,主要考虑哪些结构呢?
类加载器 和 执行引擎 -
类的加载过程分析图:
-
加载
- 通过一个类的权限定名获取定义此类的二进制字节流。
- 将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构。
- 在内存中生成一个代表这个类的 java.lang.Class 对象,做为方法区这个类的各种数据的访问入口。
- 链接
- 验证(Verify)
- 目的在于确保 Class 文件的字节流中包含信息符合当前虚拟机要求,保证被加载类的正确性,不会危害虚拟机自身安全。
- 主要包括四种验证:文件格式验证,元数据验证,字节码验证,符号引用验证。
- 准备(Prepare)
- 为类变量分配内存并且设置该类变量的默认初始值,即零值。
- 这里不包含用 final 修饰的 static , 因为 final 在编译的时候就会分配了,准备阶段会显示初始化。
- 这里不会为实例变量初始化,类变量会分配在方法区中,而实例变量会随着对象一起分配到 java 堆中。
- 解析(Resolve)
- 将常量池内的符号引用转换为直接引用的过程。
- 事实上,解析操作往往会伴随着 JVM 在执行完初始化再执行。
- 符号引用就是一组符号来描述所引用的目标。符号引用的字面量形式明确定义在《java 虚拟机规范》的 Class 文件格式中。直接引用就是直接指向目标的指针、相对偏移量或一个间接定位到目标的句柄。
- 解析动作主要针对类或接口、字段、类方法、接口方法、方法类型等对应常量池中的 CONSTANT_Class_info、CONSTANT_Fieldref_info、CONSTANT_Methodref_info 等。
- 初始化(Initialization)
- 初始化阶段就是执行类构造器方法 <clinit>() 的过程。
- 此方法不需要定义,是 javac 编译器自动收集类中所有类变量的赋值动作和静态代码块中的语句合并而来。
- 构造器方法中指令按语句在源文件中出现的顺序执行。
- <clinit>() 不同于类构造器。(关联:构造器是虚拟机视角下的<init>())
- 若该类具有父类,JVM 会保证子类的<clinit>()执行前,父类的<clinit>()已经执行完毕。
- 虚拟机必须保证一个类的<clinit>()方法在多线程下被同步加锁。
- 类加载器
- 引导类加载器
- 自定义类加载器
类加载的分类:
- 双亲委派机制
- 如果一个类加载器收到了类加载请求,它并不会自己先去加载,而是把这个请求委托给父类的加载器器制定;
- 如果父类加载器还存在其父类加载器,则进一步向上委托,依次递归,请求最终到达顶层的启动类加载器;
- 如果父类加载器可以完成类加载任务,就成功返回,倘若父类加载器无法完成此加载任务,子加载器才会尝试自己去加载,这就是双亲委派模式。