JVM的运行内存结构
虚拟机栈
当方法执行时,会在虚拟机中压入一个栈帧,其中主要存储了(局部变量表,操作数站,动态链接,方法出口),方法中的数据存储在局部变量表中。 数据单元为32位(局部变量槽Slot), 如果一个数据长度为(64位)如 (long,double)基本类型。则占用两个Slot。
方法区
方法区存储了加载到JVM 的类信息,类的元数据信息主要包括了类的接口,名称等描述信息,其中最主要的为(常量池表), 其中包含了各种数据的引用, 其中产生的数据实则存放在运行时常量池中。方法区中主要存储了一些以class为主要的数据。
注:jdk8中方法区的实现为元空间
你好! 这是你第一次使用 Markdown编辑器 所展示的欢迎页。如果你想学习如何使用Markdown编辑器, 可以仔细阅读这篇文章,了解一下Markdown的基本语法知识。
Class类的文件结构
ClassFile {
u4 magic; // 魔法数字,表明当前文件是.class文件,固定0xCAFEBABE
u2 minor_version; // 分别为Class文件的副版本和主版本
u2 major_version;
u2 constant_pool_count; // 常量池计数
cp_info constant_pool[constant_pool_count-1]; // 常量池内容
u2 access_flags; // 类访问标识
u2 this_class; // 当前类
u2 super_class; // 父类
u2 interfaces_count; // 实现的接口数
u2 interfaces[interfaces_count]; // 实现接口信息
u2 fields_count; // 字段数量
field_info fields[fields_count]; // 包含的字段信息
u2 methods_count; // 方法数量
method_info methods[methods_count]; // 包含的方法信息
u2 attributes_count; // 属性数量
attribute_info attributes[attributes_count]; // 各种属性
}
常量池数据
package demo;
public class TestClass {
public static int i ;
private int m;
public int inc() {
return m + 1;
}
}
编译后的TestClass.class以16进制显示
第一个数据
(0x00000008,0x00000009) 为 0x0017 代表常量池中有 22(23-1)个类的字面量和符号引用,其它具体的具体数据在加载class后置入运行时常量池中
第二个数据
0x00000009 为 0x0a(类的方法的符号引用)
也就是说 第1个index(符号引用)指向 (0x0004)常量池的偏移4个的地址 , 第2个index(符号引用)指向(0x0013)常量池偏移19个的地址。
所以常量池偏移量为0x0004的内存数据为:
其中主要是指向全限定名常量项的索引,所以这个全限定名指向了一个
显然,java中的编码默认为UTF-8, 最终该bytes 解码后的数据为java/lang/Object
第二个为index 指向了0x0019,类型为
显然,又是两个引用,第一个引用指向了0x0008, 第二个指向了 0x0009
0x0008 为 一个,0x0009 为一个 ()v 他们虽然是UTF-8类型,但JVM会对这些字符串新型解析
常量池中所有的内容
"C:\Program Files (x86)\Java\jdk1.8.0_271/bin/javap.exe" -v demo.TestClass
Classfile /E:/project/untitled/demo/target/classes/demo/TestClass.class
Last modified 2021-2-25; size 375 bytes
MD5 checksum 0b68fcee12eb3db08e1e697dc369e545
Compiled from "TestClass.java"
public class demo.TestClass
minor version: 0
major version: 52
flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
#1 = Methodref #4.#19 // java/lang/Object."<init>":()V
#2 = Fieldref #3.#20 // demo/TestClass.m:I
#3 = Class #21 // demo/TestClass
#4 = Class #22 // java/lang/Object
#5 = Utf8 i
#6 = Utf8 I
#7 = Utf8 m
#8 = Utf8 <init>
#9 = Utf8 ()V
#10 = Utf8 Code
#11 = Utf8 LineNumberTable
#12 = Utf8 LocalVariableTable
#13 = Utf8 this
#14 = Utf8 Ldemo/TestClass;
#15 = Utf8 inc
#16 = Utf8 ()I
#17 = Utf8 SourceFile
#18 = Utf8 TestClass.java
#19 = NameAndType #8:#9 // "<init>":()V
#20 = NameAndType #7:#6 // m:I
#21 = Utf8 demo/TestClass
#22 = Utf8 java/lang/Object
至此, 常量池的数据内容完毕
u4 magic; // 魔法数字,表明当前文件是.class文件,固定0xCAFEBABE
u2 minor_version; // 分别为Class文件的副版本和主版本
u2 major_version;
u2 constant_pool_count; // 常量池计数
cp_info constant_pool[constant_pool_count-1]; // 常量池内容
访问标志
0x000000d4 常量池数据的结束
(0x000000d5,0x000000d6),类访问标识 0x0021 代表了
当前类
(0x000000d6,0x000000d7) 为 0x0003 可见又指向了 常量池中的第三个
父类
(0x000000d9,0x000000da) 为 0x0004 指向了常量池中的第四个
接口数
0个
接口数组
空 则不占用空间
字段数量
2个
字段数组
public static int i ;
0x09 代表了常量+公有
0x0005代表指向了常量池中的5第五项,即"i"
0x0006代表了常量池中的第六项,即"I", 含义是int 类型
0x0000代表了属性表指针为空指针
方法数量
2个
方法数组
0x0001代表了access_flags : public , 0x0008代表了常量池中的#8,
0x0009代表了常量池中的#9,
0x0001代表了1个属性
属性的数据结构为
属性的第一个属性的 attribute_name_index 为 0x000a 即常量池中的#10
attribute_length 的长度为 0x0000002f = 16 * 2 + 15 = 47
所以接下来的47B为:
因为属性是Code 那么 Code数据表结构为:
前两个已经介绍了,第三个max_stack 为 0x0001 即最大栈为1,
max_locals 为 0x0001本地变量表最大为1(隐式传递this),
code_length 为 5
code 为
即
u2 exception_table_length;为 0
exception_table[exception_table_length]; 为 没有申请内存
u2 attributes_count;为;2
attribute_info attributes[attributes_count]为, 可以猜出那两个就是LineNumberTable 和 LocalVariableTable
LineNumberTable 的数据结构为:
所以 name_index 为
指向常量池的#11
长度为:6
LineNumberTable_info 数据结构为:
{ u2 start_pc;
u2 line_number;
}
这里由于不知道为什么,理应听该是24个字节, 这里只有6个字节, 数据展位类似这样(可能由于是x86环境?)
解析后的数据为:
下一个为0x0c 即 #12为
具体数据结构为:
LocalVariableTable_attribute {
u2 attribute_name_index;
u4 attribute_length;
u2 local_variable_table_length;
{ u2 start_pc;
u2 length;
u2 name_index;
u2 descriptor_index;
u2 index;
} local_variable_table[local_variable_table_length];
}
其中的含义为;
0x000c为 指向常量池的#12
0x0001 为一个属性长度
0x0000 为 start_pc为0, 即该变量的偏移地址
0x0005 为lenght 为 5 变量的作用域长度
0x000d 为 # 13
0x000e 为 指向常量池的 #14
0x0000 为 该变量在变量槽的位置
这里有个问题就是 解析LineNumberTable 的时候和解析LocalVariableTable与字节码,和 字节码的大小对应不上。
第二个方法为
复习一下,即共有#15
#16的int返回值,
有1个属性, 属性为0x0a = #10 = Code
Code的数据结构为
Code_attribute {
u2 attribute_name_index;
u4 attribute_length;
u2 max_stack;
u2 max_locals;
u4 code_length;
u1 code[code_length];
u2 exception_table_length;
{ u2 start_pc;
u2 end_pc;
u2 handler_pc;
u2 catch_type;
} exception_table[exception_table_length];
u2 attributes_count;
attribute_info attributes[attributes_count];
}
属性长度为:0x0031 = 16*3 + 1 = 49
2个栈
1个变量表
…
属性数量
1个
0x11 = #17
即
接下来很明显
Class至此解析完毕