局部变量表: 存放了编译期可知的各种数据类型(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文件常量池主要存放两大常量:字面量和符号引用。
-
字面量: 字面量分为文本字符串(如: "abc",1等)和用final修饰的成员变量(实例变量和静态变量)
-
符号引用: 符号引用包括三种:类的全限定名,方法名和描述符,字段名和描述符。
运行时常量池
运行时常量池是在类加载阶段,将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虚拟机对方法区的实现为永久代。
在JDK1.7字符串常量池和静态变量被从方法区拿到了堆中,运行时常量池剩下的还在方法区, 也就是hotspot中的永久代。
在JDK8 hotspot移除了永久代用元空间(Metaspace)取而代之, 这时候字符串常量池还在堆,运行时常量池还在方法区,只不过方法区的实现从永久代变成了元空间(Metaspace)