JVM字节码结构文件剖析
提问:java中对多能实现多少个接口?
00 00==>FFFF==>15*16^3+15*16^2+15*16+15
class常量池类型分类
一:源代码
package com.tuling.smlz.jvm.classbyatecode;
/**
* Created by smlz on 2019/11/5.
*/
public class TulingByteCode {
private String userName;
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
}
二:通过我们javap -verbose TulingByteCode .class反编译
//表示我们通过反编译的来源是哪个字节码文件
Classfile /D:/work_space/idea_space/spring-cloud-source/tuling-jvm/target/classes/com/tuling/smlz/jvm/classbyatecode/TulingByteCode.class
//最后修改日期;文件大小
Last modified 2019-11-5; size 629 bytes
//文件的md5值
MD5 checksum a0a9c001787f00738627278b0946a388
//.class文件是通过哪个源文件编译过来的
Compiled from "TulingByteCode.java"
//字节码的详细信息
public class com.tuling.smlz.jvm.classbyatecode.TulingByteCode
//jdk的次版本号
minor version: 0
//jdk的主版本号
major version: 52
//访问权限
flags: ACC_PUBLIC, ACC_SUPER
//常量池
Constant pool:
#1 = Methodref #4.#21 // java/lang/Object."<init>":()V
#2 = Fieldref #3.#22 // com/tuling/smlz/jvm/classbyatecode/TulingByteCode.userName:Ljava/lang/String;
#3 = Class #23 // com/tuling/smlz/jvm/classbyatecode/TulingByteCode
#4 = Class #24 // java/lang/Object
#5 = Utf8 userName
#6 = Utf8 Ljava/lang/String;
#7 = Utf8 <init>
#8 = Utf8 ()V
#9 = Utf8 Code
#10 = Utf8 LineNumberTable
#11 = Utf8 LocalVariableTable
#12 = Utf8 this
#13 = Utf8 Lcom/tuling/smlz/jvm/classbyatecode/TulingByteCode;
#14 = Utf8 getUserName
#15 = Utf8 ()Ljava/lang/String;
#16 = Utf8 setUserName
#17 = Utf8 (Ljava/lang/String;)V
#18 = Utf8 MethodParameters
#19 = Utf8 SourceFile
#20 = Utf8 TulingByteCode.java
#21 = NameAndType #7:#8 // "<init>":()V
#22 = NameAndType #5:#6 // userName:Ljava/lang/String;
#23 = Utf8 com/tuling/smlz/jvm/classbyatecode/TulingByteCode
#24 = Utf8 java/lang/Object
{
//构造方法
public com.tuling.smlz.jvm.classbyatecode.TulingByteCode();
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 6: 0
LocalVariableTable:
Start Length Slot Name Signature
0 5 0 this Lcom/tuling/smlz/jvm/classbyatecode/TulingByteCode;
//get方法
public java.lang.String getUserName();
descriptor: ()Ljava/lang/String;
flags: ACC_PUBLIC
Code:
stack=1, locals=1, args_size=1
0: aload_0
1: getfield #2 // Field userName:Ljava/lang/String;
4: areturn
LineNumberTable:
line 11: 0
LocalVariableTable:
Start Length Slot Name Signature
0 5 0 this Lcom/tuling/smlz/jvm/classbyatecode/TulingByteCode;
//set方法
public void setUserName(java.lang.String);
descriptor: (Ljava/lang/String;)V
flags: ACC_PUBLIC
Code:
stack=2, locals=2, args_size=2
0: aload_0
1: aload_1
2: putfield #2 // Field userName:Ljava/lang/String;
5: return
LineNumberTable:
line 15: 0
line 16: 5
LocalVariableTable:
Start Length Slot Name Signature
0 6 0 this Lcom/tuling/smlz/jvm/classbyatecode/TulingByteCode;
0 6 1 userName Ljava/lang/String;
MethodParameters:
Name Flags
userName
}
SourceFile: "TulingByteCode.java"
三:class文件通过16进制查看器打开如下
通过16进制查看器打开的文件结构是一个当个字节来显示,因为一个16进制数可以通过4位来表示,一个字节8位可以表示二个16进制数
我们class文件结构图
Class文件结构参照表:
Class文件结构伪代码
3.1)我们通过javap -verbose来分析一个字节码的时候,将会分析字节码文件的魔数,主 次版本号,常量池,类信息,类的构造方法,类的中的方法信息,类变量与成员变量等信息.
魔数: 文件的开头的 四个字节 是固定 值位 0xCAFEBABE
3.2)次版本号(minor version):二个字节00 00 表示jdk的次版本号
3.3)主版本号(major version):二个字节 00 34 表示为jdk的主版本号,34对于10进制为52
那么52代表的是1.8,51代表的是1.7 等等一直类推下去
所以通过主次版本号来确定我们jdk的版本是1.8.0
3.4)常量池入口,占用二个字节,表示常量池中的个数=00 19 (25)-1=24个, 为啥需要-1,因为常量池中的第0个位置被我们的jvm占用了表示为null 所以我们通过编译出来的常量池索引是从1开始的.
Constant pool:
#1 = Methodref #4.#21 // java/lang/Object."<init>":()V
#2 = Fieldref #3.#22 // com/tuling/smlz/jvm/classbyatecode/TulingByteCode.userName:Ljava/lang/String;
#3 = Class #23 // com/tuling/smlz/jvm/classbyatecode/TulingByteCode
#4 = Class #24 // java/lang/Object
#5 = Utf8 userName
#6 = Utf8 Ljava/lang/String;
#7 = Utf8 <init>
#8 = Utf8 ()V
#9 = Utf8 Code
#10 = Utf8 LineNumberTable
#11 = Utf8 LocalVariableTable
#12 = Utf8 this
#13 = Utf8 Lcom/tuling/smlz/jvm/classbyatecode/TulingByteCode;
#14 = Utf8 getUserName
#15 = Utf8 ()Ljava/lang/String;
#16 = Utf8 setUserName
#17 = Utf8 (Ljava/lang/String;)V
#18 = Utf8 MethodParameters
#19 = Utf8 SourceFile
#20 = Utf8 TulingByteCode.java
#21 = NameAndType #7:#8 // "<init>":()V
#22 = NameAndType #5:#6 // userName:Ljava/lang/String;
#23 = Utf8 com/tuling/smlz/jvm/classbyatecode/TulingByteCode
#24 = Utf8 java/lang/Object
3.4.1)常量池结构表如图所示
u1,u2,u4,u8分别代表1个字节,2个字节,4个字节,8个字节的无符号数
3.4.2)我们的常量池可以看作我们的java class类的一个资源仓库(比如Java类定的方法和变量信息),我们后面的方法 类的信息的描述信息都是通过索引去常量池中获取。
1)常量池中主要存放二种常量,一种是字面量 一种是符号引用
3.4.3)在JVM规范中,每个字段或者变量都有描述信息,描述信息的主要作用是 数据类型,方法参数列表,返回值类型等.
1)基本参数类型和void类型都是用一个大写的字符来表示,对象类型是通过一个大写L加全类名表示,这么做的好处就是在保证jvm能读懂class文件的情况下尽量的压缩class文件体积.
基本数据类型表示:
B---->byte
C---->char
D---->double
F----->float
I------>int
J------>long
S------>short
Z------>boolean
V------->void
对象类型:
String------>Ljava/lang/String;(后面有一个分号)
对于数组类型: 每一个唯独都是用一个前置 [ 来表示
比如: int[] ------>[ I,
String [][]------>[[Ljava.lang.String;
2)用描述符来描述方法的,先参数列表,后返回值的格式,参数列表按照严格的顺序放在()中
比如源码 String getUserInfoByIdAndName(int id,String name) 的方法描述符号
(I,Ljava/lang/String;)Ljava/lang/String;