Java虚拟机管理的内存包括几个运行时数据内存:程序计数器、虚拟机栈、本地方法栈、堆和方法区,其中方法区和堆是线程共享的数据区,其他几个是线程隔离的数据区。
一、程序计数器(线程私有)
程序计数器是一块较小的内存空间,可以看成是当前线程执行字节时的行号指示器。
每条线程都需要有一个独立的程序计数器,这是因为多线程是通过轮流切换并分配处理器执行时间的方式工作的,每条线程都需要记录当前执行位置以便下一次能够继续执行。
不同线程的计数器独立存储,互不干扰,我们称这类内存区域为“线程私有”的内存。此内存区域是唯一一个在Java虚拟机规范中没有规定任何OutOfMemoryError情况的区域。
二、虚拟机栈(线程私有)
虚拟机栈也是线程私有的,它的生命周期与线程相同。
每个方法在执行的同时会创建一个栈帧,用于存储局部变量表、操作数栈、动态链接、方法出口等信息。每个方法从开始到结束,就对应着一个栈帧在虚拟机栈中入栈到出栈的过程。
1、如果线程请求的栈深度大于虚拟机允许的深度,则会抛出StackOverflowError异常。
2、如果虚拟机栈可以动态扩展,然而在扩展时无法申请到足够的内存空间,则会抛出OutOfMemoryError异常。
三、本地方法栈(线程私有)
与虚拟机栈作用十分相似,区别在于虚拟机栈是为虚拟机执行Java方法服务,而本地方法栈则是为虚拟机用到的Native方法服务。本地方法栈也会抛StackOverflowError和OutOfMemoryError异常。
四、Java堆(线程共享)
对于大多数应用来说,Java堆是虚拟机管理的内存中最大的一块,是被所有线程共享的一块区域,在虚拟机启动时创建。这个区域的唯一作用就是存放对象实例,几乎所有的对象实例都在这里分配内存。
Java堆是垃圾回收器重点管理的区域,因此很多时候也被称为GC堆。
从内存回收的角度来看,现在垃圾回收基本采用的是分代回收算法,因此Java堆还可以细分为新生代和老生代;
从内存分配的角度来看,线程共享的Java堆中可能划分出多个线程私有的分配缓冲区。
划分的目的都是为了更好地回收内存或者更快地分配内存,存放的仍然是对象实例。
五、方法区(别名:非堆)(线程共享)
方法区和堆一样,也是各个线程共享的一块内存区域,用于存储已被加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。