java 运行时内存区域
内存区域分为以下几个:
- 程序计数器
- 虚拟机栈
- 本地方法栈
- 堆
- 方法区
程序计数器
存储了下一个指令的地址,是 线程 私有的, 生命周期 与线程一样。
虚拟机栈
虚拟机栈是线程私有的,其生命周期与线程一样。
java在运行时,虚拟机栈会同时创建一个栈帧,用于存储 局部变量 表 、 操作数栈 、 动态连接 、 方法出口 等信息。
每当调用一个方法时,就会对应一个栈帧在虚拟机栈中从入栈到出栈。(调式的时候非常明显)
我们平常说的“栈”和“堆”,其中“栈”指定就是虚拟机栈,更具体的说是虚拟机栈中的“局部变量表”。
- 局部变量表
局部变量表中存放了基本数据类型( boolean 、 byte 、 char 、 short 、int、long、float、double)、
对象引用类型、returnAddress类型。
局部变量表所需的空间是在编译期间完成的
- 局部变量槽(Slot)
数据类型在局部变量表中的大小是按槽的个数决定的。比如:64位长度的long和double类型的数据会占用两个变量槽,其余的数据类型只占用一个。
槽相当于单位,一个槽大小是32比特或者64比特,槽的大小由虚拟机本身决定,不同的虚拟机的槽各不一样。
- 异常
局部变量表中存在两种异常:
StackOverflowError异常:线程请求的深度大于虚拟机所允许的深度。
OutOfMemory Error异常:如果 Java 虚拟机可以动态扩展,当栈扩展时无法申请到足够的内存会抛出此异常。
本地方法栈
线程私有的。 虚拟机栈和本地方法栈的作用类似,其区别只是虚拟机栈为虚拟机执行Java方法( 字节码 )服务,而本地方法栈是为虚拟机使用到本地(Native)方法服务。
本地方法栈也会抛出OutofMemoryError异常和StackOverflowError异常
Java堆
线程共享
几乎所有的对象实例以及数组都在堆上分配
方法区
线程共享
用于存储已被虚拟机加载的类型信息、常量、静态变量、即时编译器编译后的代码缓存等数据。
常量池就是在方法区中
内存溢出异常
StackOverflowError异常
如果线程请求的栈深度大于虚拟机所允许的最大深度,将抛出StackOverflowError异常。
倘若存在大量的变量,或者方法中的操作非常多,归结就是栈帧足够大,将会超出栈(虚拟机栈)允许栈帧的最大容量(抛出StackOverflowError异常)。我们可以通过参数- Xss 来改变栈的内存容量。
OutOfMemoryError异常
如果虚拟机的栈内存允许动态扩展,当扩展栈容量无法申请到足够的内存时,将抛出
OutOfMemoryError异常。
- Java堆内存溢出: Java堆存放的是实例对象,如果实例对象足够多,会导致堆内存溢出,堆具有大小,倘若超出此大小必然导致堆内存溢出。有些虚拟机具有自动扩展功能(堆最小值-Xms参数和最大值-Xmx参数,倘若最小值和最大值一样,可避免堆自动扩展)
内存泄漏 和内存溢出
- 内存泄漏:存在对象实例无法被垃圾回收机制回收。当对象实例被引用了,但引用该对象实例的对象没有被引用。