JVM字节码技术
JVM字节码技术
不积细流无以成江河
1.什么是字节码为什么要学习字节码
Java 字节码:
bytecode,是Java代码编译后的中间代码格式。JVM需要读取并解析字节码才能执行相应的任务。JVM字节码是JVM的指令集。JVM加载字节码格式的class文 件,校验之后通过编译器转换为本地机器代码执行。
java bytecode 由单个字节组成(所以java的操作码又叫做字节码),所以最多有256个操作吗。实际上Java只使用了200左右的操作码, 还有一些操作码则保留给调试操作。
分类(指令性质):
- 程序流程控制指令
- 算术运算指令以及类型转换指令
- 对象操作指令
- 栈操作指令 (包括局部变量交互指令)
学习字节码的好处?
1.了解字节码及其工作原理,对于编写高性能代码至关重要。
2.对于深入分析和排查问题也有一定作用,所以我们要想深入了解JVM来说,了解
字节码也是夯实基础的一项基本功
3.同时对于我们开发人员来时,不了解平台的底层原理和实现细节,想要职业进阶绝对不是长久之计,毕竟我们都希望成为更好的程序
员,。
2.获取字节码齐清单
java代码:
public class HelloByteCode {
public static void main(String[] args) {
HelloByteCode helloByteCode = new HelloByteCode();
}
}
2.1编译反编译(助记符)
javac HelloByteCode.java
javap -c HelloByteCode
警告: 二进制文件HelloByteCode包含com.zhang.demo.HelloByteCode
Compiled from "HelloByteCode.java"
public class com.zhang.demo.HelloByteCode {
//上边为反编译之后的类, 原类里边只有一个方法 编译反编译后 就出现了两个方法,我们都知道再某些情况下 会自动 生成构造方法,这里验证了构造方法是在编译期间自动生成的 。
public com.zhang.demo.HelloByteCode();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
public static void main(java.lang.String[]);
Code:
0: new #2 // class com/zhang/demo/HelloByteCode
3: dup
4: invokespecial #3 // Method "<init>":()V
7: astore_1
8: return
}
2.2 查看常量池信息
常量池 大家应该都听说过, 英文是 Constant pool 。这里做一个强调: 大多数时候 指的是 运行时常量池 。但运行时常量池里面的常量是从哪里来的呢? 主要就是由 class 文件中的 常量池结构体 组成的。
javap -c -verbose HelloByteCode
警告: 二进制文件HelloByteCode包含com.zhang.demo.HelloByteCode
Classfile /Users/zy1994/Desktop/zhangyang/AAAA-jk/jvm/code/demo/src/main/java/com/zhang/demo/HelloByteCode.class
Last modified 2020-10-30; size 303 bytes
MD5 checksum 2a667aec3dbde61f2a4ffd2412d0acd8
Compiled from "HelloByteCode.java"
public class com.zhang.demo.HelloByteCode
minor version: 0
major version: 52
flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
#1 = Methodref #4.#13 // java/lang/Object."<init>":()V
#2 = Class #14 // com/zhang/demo/HelloByteCode
#3 = Methodref #2.#13 // com/zhang/demo/HelloByteCode."<init>":()V
#4 = Class #15 // java/lang/Object
#5 = Utf8 <init>
#6 = Utf8 ()V
#7 = Utf8 Code
#8 = Utf8 LineNumberTable
#9 = Utf8 main
#10 = Utf8 ([Ljava/lang/String;)V
#11 = Utf8 SourceFile
#12 = Utf8 HelloByteCode.java
#13 = NameAndType #5:#6 // "<init>":()V
#14 = Utf8 com/zhang/demo/HelloByteCode
#15 = Utf8 java/lang/Object
SourceFile: "HelloByteCode.java"
major version: 52 (jdk的版本是从45开始的 所以当前的jdk版本是8)
flags: ACC_PUBLIC, ACC_SUPER
ACC_PUBLIC 说明该类是一个PUBLIC的类 ,ACC_SUPER 仅仅是jdk的历史遗留问题需要一直加上的。
我们可以看到常量池中有大量的#号,这就是通常所说的符号引用,因为在类加载之前jvm是不知道这些类方法的具体地址的,所以就需要用一个符号引用先进性代替,再链接的解析阶段这些符号引用才会转化成具体的引用。
那下边的这天语句来进行解析一下常量池:
#1 = Methodref #4.#13 // java/lang/Object.""????)V
- Methodref 当前符号引用(常量引用)指向的是一个方法。
- 记下来可以看到是调用一个类的方法 根据常量表查找可以看到是Object类的init方法 当然后边的注释也进行了解读,方法的返回值 V 就是没有返回值的意思。
2.3 查看方法信息
栈帧(Frame)模型 java的线程栈是由多个栈帧组成的,每一次方法调用都会生成一个栈帧 压入栈中
下图为栈帧的构成:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-U8P4myR5-1604280603056)(/Users/zy1994/Library/Application Support/typora-user-images/image-20201030165131966.png)]
栈帧主要有三个部分构成:
- 局部变量数组
- 存储的是局部变量的值或者引用 包含了方法的参数和局部变量的参数 (编译时确定)
- 操作数栈
- FILO的数据结构主要是执行jvm的操作指令
- class引用
编译和反编译
javac -g xxx
javap -c -verbose xxx
创建对象指令
public com.zhang.demo.HelloByteCode();
descriptor: ()V
flags: ACC_PUBLIC
Code:
stack=1, locals=1, args_size=1
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
LineNumberTable:
line 8: 0
LocalVariableTable:
Start Length Slot Name Signature
0 5 0 this Lcom/zhang/demo/HelloByteCode;
上一篇: 安装traefik流量监控与反向代理
下一篇: k8s使用Traefik发布服务