Java虚拟机在执行Java程序的过程中会把它所管理的内存划分为若干个不同的数据区域。这些区域有各自的用途,以及创建和销毁的时间,有的区域随着虚拟机进程的启动而一直存在,有些区域则是依赖用户线程的启动和结束而建立和销毁。
根据《Java虚拟机规范》的规定,Java虚拟机所管理的内存,将会包括以下几个运行时数据区域:
Java虚拟机栈
在Java虚拟机中,栈主要解决的是程序的运行问题,即程序如何执行,如何处理数据,负责的是运算逻辑。
Java虚拟机栈也是栈,就是JVM中的一块内存,也是符合栈的规则 —— 先进后出规则,Java虚拟机栈(JavaVirtualMachineStack)也是线程私有的,生命周期与线程同步。
虚拟机栈描述的是Java方法执行的线程内存模型:每个方法被执行的时候,Java虚拟机都会同步创建一个栈帧(StackFrame)用于存储局部变量表、操作数栈、动态连接、方法出口等信息。
什么是栈帧?
调用函数的时候,我们会为函数开辟一块内存,这块内存叫做栈帧,这个内存在Java虚拟机栈中开辟。
每一个方法被调用直至执行完毕的过程,就对应着一个栈帧在虚拟机栈中从入栈到出栈的过程。
对于每个执行线程,会分配一个Java栈,JVM在执行过程当中,每执行一个方法,都会为方法在当前栈中增加一个栈帧,每个栈帧的信息与具体实现相关,但一般会由3部分组成:变量区,方法参数和本地变量会放入这个位置,大小是固定的,在进行方法时会先分配好,在类定义中,会由max local来指定这块区的大小;方法信息区,会包括当前类常量池的入口地址等信息,这块大小也是固定的;操作栈,与Intel体系架构中的运算使用寄存器来进行不一样,JVM的字节码的方法调用、运算等需要的参数,都是通过操作栈来传递的。在类定义中,会由max stack指定最大的操作栈。
方法区:
方法区(Method Area)与Java堆一样,是各个线程共享的内存区域,它用于存储已被虚拟机加载的类型信息、常量、静态变量、即时编译器编译后的代码缓存等数据。
Java堆:
Java堆是被所有线程共享的一块内存区域,在虚拟机启动时创建。此内存区域的唯一目的就是存放对象实例,Java 世界里“几乎”所有的对象实例都在这里分配内存,创建的对象信息将放入堆中,堆内部如何实现各虚拟机各不相同。
Java堆是垃圾收集器管理的内存区域,因此一些资料中它也被称作“GC堆”。
本地方法栈:
对本地方法的调用,并不会使用Java栈而是使用本地方法栈,本地方法栈的组成取决于所使用的平台和操作系统。
PC寄存器/程序计数器:
对于每个执行线程会分配一个PC寄存器,寄存器中存放当前字节码的执行位置,通过改变这个计数器的值来选取下一条需要执行的字节码指令。
由于Java虚拟机的多线程是通过线程轮流切换、分配处理器执行时间的方式来实现的,在任何一个确定的时刻,一个处理器(对于多核处理器来说是一个内核)都只会执行一条线程中的指令。因此,为了线程切换后能恢复到正确的执行位置,每条线程都需要有一个独立的程序计数器,各条线程之间计数器互不影响。
独立存储,我们称这类内存区域为 “线程私有” 的内存。