Java虚拟机内存区域分布和字节码简析
虚拟机内存区域
解读字节码文件
测试类代码:
package com.momingqi;
public class Custom {
String m1 = "I love you";
String m2 = "I like you";
int i2 = 1024;
short i3 = 10;
public static final String haha = "hahahaha";
public static void say() {
System.out.println(Custom.haha);
}
public String getString(String key) {
if (key.equals("m1"))
return m1;
else {
return "what the fuck";
}
}
}
控制台命令:
javac Custom.java
javap -verbose Custom.class
控制台输出的字节码
C:\Users\mingC\Desktop>javap -verbose Custom.class
Classfile /C:/Users/mingC/Desktop/Custom.class
Last modified 2017-12-26; size 862 bytes
MD5 checksum 1b8103d38f9c253db7ace7647ab688d4
Compiled from "Custom.java"
public class com.momingqi.Custom
minor version: 0
major version: 52
flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
#1 = Methodref #15.#35 // java/lang/Object."<init>":()V
#2 = String #36 // I love you
#3 = Fieldref #9.#37 // com/momingqi/Custom.m1:Ljava/lang/String;
#4 = String #38 // I like you
#5 = Fieldref #9.#39 // com/momingqi/Custom.m2:Ljava/lang/String;
#6 = Fieldref #9.#40 // com/momingqi/Custom.i2:I
#7 = Fieldref #9.#41 // com/momingqi/Custom.i3:S
#8 = Fieldref #42.#43 // java/lang/System.out:Ljava/io/PrintStream;
#9 = Class #44 // com/momingqi/Custom
#10 = String #45 // hahahaha
#11 = Methodref #46.#47 // java/io/PrintStream.println:(Ljava/lang/String;)V
#12 = String #16 // m1
#13 = Methodref #48.#49 // java/lang/String.equals:(Ljava/lang/Object;)Z
#14 = String #50 // what the fuck
#15 = Class #51 // java/lang/Object
#16 = Utf8 m1
#17 = Utf8 Ljava/lang/String;
#18 = Utf8 m2
#19 = Utf8 i2
#20 = Utf8 I
#21 = Utf8 i3
#22 = Utf8 S
#23 = Utf8 haha
#24 = Utf8 ConstantValue
#25 = Utf8 <init>
#26 = Utf8 ()V
#27 = Utf8 Code
#28 = Utf8 LineNumberTable
#29 = Utf8 say
#30 = Utf8 getString
#31 = Utf8 (Ljava/lang/String;)Ljava/lang/String;
#32 = Utf8 StackMapTable
#33 = Utf8 SourceFile
#34 = Utf8 Custom.java
#35 = NameAndType #25:#26 // "<init>":()V
#36 = Utf8 I love you
#37 = NameAndType #16:#17 // m1:Ljava/lang/String;
#38 = Utf8 I like you
#39 = NameAndType #18:#17 // m2:Ljava/lang/String;
#40 = NameAndType #19:#20 // i2:I
#41 = NameAndType #21:#22 // i3:S
#42 = Class #52 // java/lang/System
#43 = NameAndType #53:#54 // out:Ljava/io/PrintStream;
#44 = Utf8 com/momingqi/Custom
#45 = Utf8 hahahaha
#46 = Class #55 // java/io/PrintStream
#47 = NameAndType #56:#57 // println:(Ljava/lang/String;)V
#48 = Class #58 // java/lang/String
#49 = NameAndType #59:#60 // equals:(Ljava/lang/Object;)Z
#50 = Utf8 what the fuck
#51 = Utf8 java/lang/Object
#52 = Utf8 java/lang/System
#53 = Utf8 out
#54 = Utf8 Ljava/io/PrintStream;
#55 = Utf8 java/io/PrintStream
#56 = Utf8 println
#57 = Utf8 (Ljava/lang/String;)V
#58 = Utf8 java/lang/String
#59 = Utf8 equals
#60 = Utf8 (Ljava/lang/Object;)Z
{
java.lang.String m1;
descriptor: Ljava/lang/String;
flags:
java.lang.String m2;
descriptor: Ljava/lang/String;
flags:
int i2;
descriptor: I
flags:
short i3;
descriptor: S
flags:
public static final java.lang.String haha;
descriptor: Ljava/lang/String;
flags: ACC_PUBLIC, ACC_STATIC, ACC_FINAL
ConstantValue: String hahahaha
public com.momingqi.Custom();
descriptor: ()V
flags: ACC_PUBLIC
Code:
stack=2, locals=1, args_size=1
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: aload_0
5: ldc #2 // String I love you
7: putfield #3 // Field m1:Ljava/lang/String;
10: aload_0
11: ldc #4 // String I like you
13: putfield #5 // Field m2:Ljava/lang/String;
16: aload_0
17: sipush 1024
20: putfield #6 // Field i2:I
23: aload_0
24: bipush 10
26: putfield #7 // Field i3:S
29: return
LineNumberTable:
line 3: 0
line 5: 4
line 6: 10
line 7: 16
line 8: 23
public static void say();
descriptor: ()V
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=2, locals=0, args_size=0
0: getstatic #8 // Field java/lang/System.out:Ljava/io/PrintStream;
3: ldc #10 // String hahahaha
5: invokevirtual #11 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
8: return
LineNumberTable:
line 13: 0
line 14: 8
public java.lang.String getString(java.lang.String);
descriptor: (Ljava/lang/String;)Ljava/lang/String;
flags: ACC_PUBLIC
Code:
stack=2, locals=2, args_size=2
0: aload_1
1: ldc #12 // String m1
3: invokevirtual #13 // Method java/lang/String.equals:(Ljava/lang/Object;)Z
6: ifeq 14
9: aload_0
10: getfield #3 // Field m1:Ljava/lang/String;
13: areturn
14: ldc #14 // String what the fuck
16: areturn
LineNumberTable:
line 17: 0
line 18: 9
line 20: 14
StackMapTable: number_of_entries = 1
frame_type = 14 /* same */
}
SourceFile: "Custom.java"
首先看到字节码头几行是文件信息,包括编译版本、修饰符等。
下面分析字节码的常量池(Constant pool)
- 用#号加数字的形式来表示一个常量
- 常量之间可以相互引用,
- 这些常量包括:字段名称、方法名称、类名等等
- 这些常量存放在方法区的运行时常量池中
构造方法解析
方法属性信息:
- stack=2,代表操作数栈的深度为2
- locals=1,代表局部变量表的大小为1个slot(可以理解为一个32位数据),slot可以复用
- args_size=1,代表参数数目,实例方法隐藏参数‘this’,所以这里参数大小为1
LineNumberTable
代表的是字节码行数(字节码的偏移量)和Java源码的对应关系,这不是必需的
方法体
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
- 指令前面的数字代表字节码偏移量,可以忽略;
- 带井号的是指令的参数
- aload_0 代表将0号变量放入操作数栈中,在这里0号参数是“this”指令
- 第一行调用指令invokespecial,这个指令用来调用对象的特殊方法,这里的特殊方法是这个对象的构造方法
上面两局结合起来就是从局部变量表中取出第0号变量,然后调用构造方法,而第0号变量就是这个构造方法的参数。
4: aload_0
5: ldc #2 // String I love you
7: putfield #3 // Field m1:Ljava/lang/String;
- ldc是从运行时常量池中加载一个常量到操作数栈中
- putfield是字面意思,给对象的字段赋值
上面代码的执行过程是:先加载局部变量表中第0号变量,在加载常量池中的字符串,然后调用方法putfield给字段m1赋值