JVM内存模型详情解析
一、结构图
NOTE: string常量池 存在 堆内存中
二、各部分详情解析
1、堆
1)老年代
对象年龄(经过一次 monitor GC 年龄加1) >15 的会存到 老年代
2)年轻代
- eden
- 内存分配默认是 年轻代 80%
- survivor
- 内存分配默认是 年轻代20%(from 和 to 区分别占10%)
3)常量池:
String常量 存储在堆中
2、虚拟机栈
1)局部变量表
- 存储的数据:存放方法参数和方法内定义的局部变量
- note:局部变量表的容量以变量槽(Variable Slot)为最小单位,Java程序编译为Class文件时,就在方法的Code属性中的max_locals数据项中确定了该方法所需分配的局部变量表的最大容量。(最大Slot数量)
2)操作数栈
- 存储的数据
- 和局部变量区一样,操作数栈也是被组织成一个以字长为单位的数组。但是和前者不同的是,它不是通过索引来访问,而是通过标准的栈操作—压栈和出栈—来访问的
- note:操作数栈的最大深度也在编译的时候写入到方法的Code属性的max_stacks数据项中,操作数栈的深度都不会超过max_stacks中设置的最大值。
3)动态链接
- **介绍:**Java 是在运行期间动态链接的,所以为了支持动态链接,需要将方法区里面的符号引用转为直接引用(即:给出地址),这就叫动态链接
4)方法出口信息
note:栈中每一个线程 都会创建自己的 栈帧,每个栈帧中又会分为以上四种信息
源码及字节码解析栈
3、本地方法栈
和虚拟栈类似,只是存储native修饰的 方法信息
4、元数据区/元空间
1)类信息
2)静态变量
- static修饰的变量
3)常量池
-
常量池在java用于保存在编译期已确定的,已编译的class文件中的一份数据。它包括了关于类,方法,接口等中的常量,也包括字符串常量,如String s = "java"这种申明方式;当然也可扩充,执行器产生的常量也会放入常量池,故认为常量池是JVM的一块特殊的内存空间。
-
Java是一种动态链接的语言,常量池的作用非常重要,常量池中除了包含代码中所定义的各种基本类型(如int、long等等)和对象型(如String及数组)的常量值外,还包含一些以文本形式出现的符号引用,比如:
类和接口的全限定名;
字段的名称和描述符;
方法的名称和描述符。
在C语言中,如果一个程序要调用其它库中的函数,在链接时,该函数在库中的位置(即相对于库文件开头的偏移量)会被写在程序中,在运行时,直接去这个地址调用函数;
而在Java语言中不是这样,一切都是动态的。编译时,如果发现对其它类方法的调用或者对其它类字段的引用的语句,记录进class文件中的只能是一个文本形式的符号引用,在连接过程中,虚拟机根据这个文本信息去查找对应的方法或字段。
所以,与Java语言中的所谓“常量”不同,class文件中的“常量”内容很丰富,这些常量集中在class中的一个区域存放,一个紧接着一个,这里就称为“常量池”。
-
class文件共有11种常量表
常量表类型 | 标志值(占1 byte) | 描述 |
---|---|---|
CONSTANT_Utf8 | 1 | UTF-8编码的Unicode字符串 |
CONSTANT_Integer | 3 | int类型的字面值 |
CONSTANT_Float | 4 | float类型的字面值 |
CONSTANT_Long | 5 | long类型的字面值 |
CONSTANT_Double | 6 | double类型的字面值 |
CONSTANT_Class | 7 | 对一个类或接口的符号引用 |
CONSTANT_String | 8 | String类型字面值的引用 |
CONSTANT_Fieldref | 9 | 对一个字段的符号引用 |
CONSTANT_Methodref | 10 | 对一个类中方法的符号引用 |
CONSTANT_InterfaceMethodref | 11 | 对一个接口中方法的符号引用 |
CONSTANT_NameAndType | 12 | 对一个字段或方法的部分符号引用 |
(1) CONSTANT_Utf8 用UTF-8编码方式来表示程序中所有的重要常量字符串。这些字符串包括: ①类或接口的全限定名, ②超类的全限定名,③父接口的全限定名, ④类字段名和所属类型名,⑤类方法名和返回类型名、以及参数名和所属类型名。⑥字符串字面值
表格式: tag(标志1:占1byte) length(字符串所占字节的长度,占2byte) bytes(字符串字节序列)
(2) CONSTANT_Integer、 CONSTANT_Float、 CONSTANT_Long、 CONSTANT_Double 所有基本数据类型的字面值。比如在程序中出现的1用CONSTANT_Integer表示。3.1415926F用 CONSTANT_Float表示。
表格式: tag bytes(基本数据类型所需使用的字节序列)
(3) CONSTANT_Class 使用符号引用来表示类或接口。我们知道所有类名都以 CONSTANT_Utf8表的形式存储。但是我们并不知道 CONSTANT_Utf8表中哪些字符串是类名,那些是方法名。因此我们必须用一个指向类名字符串的符号引用常量来表明。
表格式: tag name_index(给出表示类或接口名的CONSTANT_Utf8表的索引)
(4) CONSTANT_String 同 CONSTANT_Class,指向包含字符串字面值的 CONSTANT_Utf8表。
表格式: tag string_index(给出表示字符串字面值的CONSTANT_Utf8表的索引)
(5) CONSTANT_Fieldref 、 CONSTANT_Methodref、 CONSTANT_InterfaceMethodref 指向包含该字段或方法所属类名的 CONSTANT_Utf8表,以及指向包含该字段或方法的名字和描述符的 CONSTANT_NameAndType 表
表格式: tag class _index(给出包含所属类名的CONSTANT_Utf8表的索引) name_and_type_index(包含字段名或方法名以及描述符的 CONSTANT_NameAndType表 的索引)
(6) CONSTANT_NameAndType 指向包含字段名或方法名以及描述符的 CONSTANT_Utf8表。
-
八种基本类型的包装类和对象池
java中基本类型的包装类的大部分都实现了常量池技术,这些类是Byte,Short,Integer,Long,Character,Boolean,另外两种浮点数类型的包装类则没有实现。另外Byte,Short,Integer,Long,Character这5种整型的包装类也只是在对应值小于等于127时才可使用常量池,也即对象不负责创建和管理大于127的这些类的对象。
5、程序计数器
1)线程执行到的行号,native方法 行号为 null
三、内存溢出
会产生内存溢出的区域
- 堆
- 栈
- 元数据区(元空间)
note:只有程序计数器不会出现内存溢出