Java JVM 内存分区

Java JVM 内存分区

hb0730 79 2021-03-05

jvm.png

局部变量表: 存放了编译期可知各种数据类型(boolean、byte、char、short、int、float、long、double)对象引用reference 类型,它不同于对象本身,可能是一个指向对象起始地址的引用指针,也可能是指向一个代表对象的句柄或其他与此对象相关的位置)。

JVM内存分哪几个区,每个区的作用是什么?

程序计数器

指向当前线程正在执行的字节码指令的地址(行号),线程CPU调度的最小单位(进程是资源分配的最小单位),CPU时间片是可以被强占的,所以要记住行号。JVM中唯一一个没有规定任何OutOfMemoryError情况的区域。

虚拟机栈

存储当前线程运行方法所需要的数据、指令和返回地址(单位栈帧) (局部变量表(编译时期确认大小),操作数栈,动态链接(多态),返回地址) 。每个方法在执行的同时都会创建一个栈帧,用于存储局部变量表、操作数栈、动态链接、方法出口等信息。

每一个方法从调用直至执行完成的过程,就对应着一个栈帧在虚拟机中入栈到出站的过程。

可能会抛出栈深度异常(StackOverflowError)或内存溢出异常(OutOfMemoryError)

本地方法栈

与虚拟机栈类似,不过是调用native方法的栈。

方法区

保存了类信息、常量、静态变量(static)、JIT、运行时常量池。有的人称为“永久代”,后改名为“元空间”。

堆是Java对象的存储区域,任何用new字段分配的Java对象实例数组,都被分配在堆上,Java堆可使用 -Xms -Xmx进行内存控制,

JVM常量池

  • Class常量池(静态常量池)

  • 运行时常量池

  • 字符串常量池(全局常量池)

  • 包装类型缓存池

    Class常量池(静态常量池)

    当Java源文件被编译后,就会生成Class字节码文件。

Class常量池就存在于Class文件中(Class文件的Constant Pool中)。

Class文件常量池主要存放两大常量:字面量和符号引用。

  1. 字面量: 字面量分为文本字符串(如: "abc",1等)和用final修饰的成员变量(实例变量和静态变量)

  2. 符号引用: 符号引用包括三种:类的全限定名,方法名和描述符,字段名和描述符。

运行时常量池

运行时常量池是在类加载阶段,将class二进制数据加载到内存, 并将数据保存到方法区,其中class文件中的常量池将保存到 运行时常量池(数据都在方法区,常量池肯定也在方法区)。 也就是说一个Class文件常量池对应一个运行时常量池。

  • 运行常量池: JDK1.7之前存在方法区,JDK1.8元空间(Metaspace)代替

字符串常量池(全局常量池)

  • 字符串常量池: JDK1.7之前存储在运行常量池中,JDK1.7之后(包含JDK1.7)字符串常量池存在堆中

包装类型缓存池

包装类缓存池并不是所有的包装类都有,并且缓存池缓存的是一定范围内的数据。 拥有包装类型缓存池的类有:Integer,Byte,Character,Long,Short, 而Float,Double,Boolean都不具有缓存池。

包装类的缓存池缓存的范围基本都为: -128 - 127之间, Character的缓存范围为 0 - 127。

常量池内存位置演化

在JDK1.7之前运行时常量池逻辑包含字符串常量池存放在方法区, 此时hotspot虚拟机对方法区的实现为永久代。 16608117845f4b94c021e06.jpg

在JDK1.7字符串常量池和静态变量被从方法区拿到了堆中,运行时常量池剩下的还在方法区, 也就是hotspot中的永久代。 18549402355f4b94c099a36.jpg

在JDK8 hotspot移除了永久代用元空间(Metaspace)取而代之, 这时候字符串常量池还在堆,运行时常量池还在方法区,只不过方法区的实现从永久代变成了元空间(Metaspace)

2387486915f4b94c125fbd.jpg