目录复制
Java内存区域
- 区域划分
- 内存溢出异常
区域划分
Java虚拟机在执行Java程序的过程中会把它所管理的内存划分为若干个不同的数据区域。根据《Java虚拟机规格》的规定,Java虚拟机所管理的内存包括以下几个运行时数据区域。
线程独占的有:程序计数器,虚拟机栈,本地方法栈
线程共享的有:堆,方法区
[程序计数器]:程序计数器,是一块较小的内存空间,保存着当前线程执行的字节码位置,每个线程工作时都有独立的计数器,只为执行Java方法服务,执行Native方法时,程序计数器为空.。
[Java虚拟机栈]:Java虚拟机栈是线程私有的,生命周期与线程相同。虚拟机栈描述的是Java方法执行的线程内存模型:执行每个方法时都会同步创建一个栈帧(Stack Frame)用于存储局部遍历表、操作数栈,动态链接和方法出口等信息。
[本地方法栈]:与虚拟机栈发挥的作用相似,区别是虚拟栈为虚拟机执行Java方法服务,而本地方法栈为虚拟机执行本地(Native)方法服务。
[堆]:虚拟机管理的内存中最大的一块,几乎所有的对象实例以及数组都在堆上分配。从内存分配的角度来看,所有线程共享的堆中可以划分出多个线程私有的分配缓冲区(Thread Local Allocation Buffer,TLAB)。
[方法区]:又称非堆区,用于存储已被虚拟机加载的类信息,常量,静态变量,即时编译器优化后的代码缓存,动态生成的类等数据.1.7的永久代和1.8的元空间都是方法区的一种实现
HotSpot虚拟机中方法区的实现:
JDK 8之前,HotSpot虚拟机设计团队选择把分代设计扩展至方法区,及使用永久代(Permanent Generation)来实现方法区。
JDK8之后采用在本地内存(Native Memory)中实现的元空间(Meta-space)代替了永久代。内容变更:
JDK 7 之前,永久代中存放的内容有:类信息,静态变量,常量,JIT编译器编译后的代码缓存等信息。
JDK7 时 ,原本存放在永久代中的 字符串常量池,静态变量等被移至Java堆中。
JDK8时,永久代剩余的内存(主要是类型信息)全部移到了元空间中。
内存溢出异常
- 程序计数器:唯一《Java虚拟机规范》中内体育规定任何OutOfMemoryError 的区域。
- 虚拟机栈:当线程请求的栈深度大于虚拟机栈允许的深度时,将抛出StackOverflowError异常;如果虚拟机容量可以动态扩展,当栈扩展时无法申请到足够的内存会抛出OutOfMemoryError异常。
- 本地方法栈:与虚拟机栈一样,本地方法栈在栈深度溢出或栈扩展失败时分别抛出 StackOverflowError 和 OutOfMemoryErro 异常
- 堆:当Java堆中没有足够内存完成实例分配,并且堆无法再扩展时,会抛出 OutOfMemoryError异常
- 方法区:如果方法区无法满足新的内存分配需求时,将抛出OutOfMemoryError异常。
直接内存:直接内存(Direct Memory)并不是虚拟机运行时数据区的一部分,也不是《Java虚拟机规范》中定义的内存区域,但由于总内存有限,当各个内存区域的总和大于物理内存时,会导致动态扩展时出现OutOfMemoryError异常。