JVM学习-字节码篇
前言
视频教程:https://www.bilibili.com/video/BV1Dz4y1A7FB?p=2&vd_source=85ac5ee1b07df12a44b648a8751d30f6
相关知识点
JVM的生命周期
1 | - 启动 |
什么是JVM
1 | - 虚拟机 |
JVM架构图/体系结构
1 | - 架构 |
1 | HotSpot虚拟机体系结构(如上图,图要会画) |
Class文件里面是什么
1 | 源代码经过编译器编译之后便会生成一个字节码文件,是一种二进制的类文件,它的内容是JVM的指令,而不像C、C++经由编译器直接生成机器码。 |
什么是编译器
1 | JVM的编译器:将源代码编译成字节码(前端编译器) |
JAVA编译器的编译步骤
1 | 词法解析、语法解析、语义解析、生成字节码 |
哪些类型对应有Class对象
1 | (1)class:外部类,成员(成员内部类,静态内部类),局部内部类,匿名内部类 |
JAVA半编译半解释型语言
1 | JAVA的执行引擎包括:解释器和JIT |
Class文件结构
1 | 魔数:每个 Class 文件开头的4个字节的无符号整数称为魔数。它的唯一作用是确定这个文件是否为一个能被虚拟机接受的有效合法的Class文件。魔数值固定为0xCAFEBABE。不会改变。 |
常量池表
- 常量池是一种表结构。主要存放两大类常量:字面量和符号引用。
- 主要包含class文件结构及其子结构中引用的所有字符串常量、类或接口名、字段名和其他常量。
- 第1个字节作为类型标记,用于确定该项的格式,这个字节称为tag byte (标记字节、标签字节)。
- 这14种表(或者常量项结构)的共同点是:表开始的第一位是一个u1类型的标志位(tag),代表当前这个常量项使用的是哪种表结构,即哪种常量类型。
- 在常量池列表中,CONSTANT_Utf8_info常量项是一种使用改进过的UTF-8编码格式来存储诸如文字字符串、类或者接口的全限定名、字段或者方法的简单名称以及描述符等常量字符串信息。
- 这14种常量项结构还有一个特点是,其中13个常量项占用的字节固定,只有CONSTANT_Utf8_info占用字节不固定,其大小由length决定。为什么呢?因为从常量池存放的内容可知,其存放的是字面量和符号引用,最终这些内容都会是一个字符串,这些字符串的大小是在编写程序时才确定,比如你定义一个类,类名可以取长取短,所以在没编译前,大小不固定,编译后,通过utf-8编码,就可以知道其长度。
符号引用、直接引用的区别
1 | Java代码在进行Javac编译的时候,并不像C和C++那样有“连接”这一步骤,而是在虚拟机加载Class文件的时候进行动态链接。也就是说,在最初生成的Class文件中不会保存各个方法、字段的最终内存布局信息,因此这些字段、方法的符号引用不经过运行期转换的话无法得到真正的内存入口地址,也就无法直接被虚拟机使用。当虚拟机运行时,需要从常量池获得对应的符号引用,再在类创建时或运行时解析、翻译到具体的内存地址之中。 |
访问标识
类索引、父类索引、接口索引集合
- this_class(类索引)
- super_class (父类索引)
- interfaces(接口索引)
- interfaces_count (接口计数器)
- interfaces [](接口索引集合)
面试题
println的结果
1 | class Father { |
输出:
Son.x = 0
Son.x = 30
20
分析:父类f指向子类Son的引用。顺序:先初始化父类,再初始化子类。
①即父类的普通代码块和普通变量:x = 30
②父类的构造方法。 ==父类的构造方法的this指向Son,调用子类的print方法,但子类的x没有赋值,默认是0,所以打印Son.x = 0。并将父类的x赋值为20==
③子类普通代码块和普通变量。子类x=30
④子类的构造方法。==子类x=30,调用子类的print方法,打印Son.x = 30。并将子类的x赋值为40==
⑤执行main中的f.x。==f类型是父类,所以取父类中的x,为20。打印20==
相关知识点:子类初始化的顺序、多态
子类初始化的顺序:
① 父类静态代码块和静态变量。
② ==子类静态代码块和静态变量。==
③ 父类普通代码块和普通变量。
④ 父类构造方法。
⑤ 子类普通代码块和普通变量。
⑥ 子类构造方法。
多态:当出现 父类 指向 子类的对象时 (向上转型)
①父类和子类出现重名的属性:向上转型的时候代表父类,所以当父类和子类出现同名的属性,对象使用的是父类的。(调用属性的时候看的是左边定义的类型)
②父类和子类出现重名的方法:看的是实际指向的子类对象在堆内存中的内容。(看右边实际创建的对象)
③可以调用父类特有的属性或方法,不能调用子类特有的属性或方法
字节码指令都有哪些
1 | 加载与存储指令:xload<n>、xstore<n>等 |
方法调用指令
- invokevirtual指令用于调用对象的实例方法,根据对象的实际类型进行分派(虚方法分派),支持多态。这也是Java语言中最常见的方法分派方式。
- invokeinterface指令用于调用接口方法,它会在运行时搜索由特定对象所实现的这个接口方法,并找出适合的方法进行调用。
- invokespecial指令用于调用一些需要特殊处理的实例方法,包括实例初始化方法(构造器)、私有方法和父类方法。这些方法都是静态类型绑定的,不会在调用时进行动态派发。
- invokestatic指令用于调用命名类中的类方法(static方法)。这是静态绑定的。
- invokedynamic:调用动态绑定的方法,这个是JDK 1.7后新加入的指令。用于在运行时动态解析出调用点限定符所引用的方法,并执行该方法。前面4条调用指令的分派逻辑都固化在 java 虚拟机内部,而 invokedynamic指令的分派逻辑是由用户所设定的引导方法决定的。
字节码指令相关面试
1 | int a = 1;JVM如何取得a的值(圆通) |
Java虚拟机中,数据类型可以分为哪几类? (阿里)
1 | 1.数据类型可以分为两种:基本类型和引用类型,基本类型的变量持有原始值,而引用类型的变量持有引用值。 |
为什么不把基本类型放堆中呢? (阿里)
1 | 首先是栈、堆的特点不同。(堆比栈要大,但是栈比堆的运算速度要快。) |
Java中的参数传递是传值呢?还是传引用? (阿里)
1 | 值传递 |
Java中有没有指针的概念? (阿里)
1 | 没有 |