Java运行时数据区域
数据区域主要分为五个部分:
- 程序计数器
 - 虚拟机栈
 - 本地方法栈
 - 堆
 - 方法区
 
其中所有线程共享区域有:方法区和堆。
每个线程独享区域有:虚拟机栈,本地方法栈,程序计数器

线程独享的区域
程序计数器
程序计数器是一块较小的内存空间,它可以看作是当前线程所执行的字节码的行号指示器,如果当前线程执行的是一个本地方法,那么此时计数器的值为undefined,是唯一一个不会出现OutOfMemoryError的内存区域。
Java虚拟机栈
虚拟机栈描述的是Java方法执行的线程内存模型,每个方法被执行的时候,Java虚拟机都会同步创建一个栈帧,用于存放以下内容:
- 局部变量表
 - 操作数栈
 - 动态链接
 - 方法出口
 
其中局部变量表包括以下内容:
- 八大基本数据类型
 - 对象引用
 - returnAddress类型(保存的是return后要执行的字节码的指令地址)
 
这些数据类型在局部变量表中的存储空间以局部变量槽来表示,64位长度的long和double类型占用两个变量槽,其余的占用一个。局部变量表所需的内存空间在编译期间完成分配,方法运行期间不会改变局部变量表的大小。
Java 虚拟机栈会出现两种异常:StackOverFlowError 和 OutOfMemoryError。
- 如果线程请求的栈深度大于虚拟机所允许的深度,将抛出
StackOverFlowError - 如果Java虚拟机栈容量可以动态扩展,当栈扩展时无法申请到足够内存会抛出
OutOfMemoryError 
本地方法栈
本地方法栈是为JVM使用到的Native方法准备的空间。
线程共享的区域
堆
主要特点:
- 堆是被所有线程共享的一块内存区域
 - 在虚拟机启动时创建
 - 堆的唯一目的就是存放对象实例
 - 堆是垃圾回收管理的内存区域
 - 可分为新生代,老年代,永久代等(一部分垃圾收集器的共同特性或者设计风格,不是具体实现的固有内存布局)
 - 从分配内存的角度看,所有线程共享的Java堆可以划分出多个线程私有的分配缓冲区(TLAB)以提升对象分配的效率,这样做可以更好地回收内存或者更快的分配内存。
 - 堆的大小可以通过
-Xmx和-Xms设定。 
方法区
Java 虚拟机规范中定义方法区是堆的一个逻辑部分。 用于存储以下内容:
- 已被虚拟机加载的类信息
 - 常量
 - 静态变量
 - 即时编译器编译后的代码
 
方法区内的主要回收目标是对常量池的回收和对类型的卸载。
运行时常量池
运行时常量池是方法区的一部分。主要存放以下内容:
- 类信息(版本,字段,方法,接口等描述信息)
 - 常量池表(用于存放编译期生成的各种字面量与符号引用)
 
Java并不要求常量一定在编译期间产生,并非Class文件中常量池的内容才能进入运行时常量池,运行期间产生的新的常量也能放入池中。比如String类的intern()方法。
当常量池无法再申请到内存会抛出OutOfMemoryError
直接内存(堆外内存)
直接内存并不是虚拟机运行时数据区的一部分,也不是虚拟机规范中定义的内存区域,但是会被频繁地使用,也可能导致OutOfMemoryError
直接内存的分配不受JVM控制,但是会受到本机总内存(物理内存,SWAP分区,分页文件)以及处理器寻址空间的限制。