Java虚拟机在执行Java程序的过程中会把它所管理的内存划分为若干个不同的数据区域。
这些区域包含:虚拟机栈、本地方法栈、程序计数器、堆、方法区。
每个区域都有各自的生命周期以及各自的作用范围及功能。
在线程中,虚拟机栈、本地方法栈、程序计数器是私有的,属于指令,堆、方法区则是共享的,属于数据。
程序计数器标识的是线程正在执行的字节码指令地址,由于多线程的存在及CPU可能会来回切换线程。
多个线程之间的程序计数器是相互独立的,可以看作是各自的行号指示器。
虚拟机栈,它表示的是Java方法在运行时候的内存模型,是一种先进后出的数据结构。虚拟机栈,存储了当前方法运行所需要的指令、数据、返回地址。

方法被调用的时候都会去创建栈帧,从方法执行的开始和结束又对应着栈帧在虚拟机栈中的入栈和出栈。栈帧被用来保存局部变量表、操作数栈、动态链接、方法出口等信息,每一个方法都对应着一个栈帧。
1 | package com.sankuai.test; |
局部变量表中包含了基本数据类型、对象引用类型、返回地址。
基本数据类型:byte、short、int、long、float、double、boolean、char。对象引用类型:对象的起始地址(数组)或者对象的句柄(类)。返回地址:指向的是一条字节码指令的地址。
操作数栈,本质也是一个栈,入栈和出栈代表着变量的操作,比如:两个变量相加。动态链接,所调用的对象会在常量池中存储并用标号标识,而常量池会指向对象的真实地址。

方法出口,当栈帧出栈之后,对应的这个方法应该返回到调用它的地方。
接着是本地方法栈,它跟虚拟机栈是类似的,只不过它调用的不是Java方法,而是Native本地方法。
另外,虚拟机栈、本地方法栈都是线程私有的,生命周期跟随着线程。
当线程越多、栈的深度越深,就可能造成栈溢出StackOverFlowError,甚至是OutOfMemoryError。
堆是Java虚拟机中内存占用最大的一块区域,也被所有的线程所共享,主要是用来存放对象实例。所有的对象实例和数组都要在堆上分配内存,不过后来出现了栈上分配、标量替换、逃逸分析等技术,使得对象不一定需要在堆上分配内存。一个堆包含了:新生代(Eden、S0、S1)、老年代,比例通常是1:2。

堆也是垃圾收集器GC的重点区域,目前普遍采用的是分代收集算法。
最后是方法区,也是被所有线程所共享的,主要存储的是:被虚拟机所加载的类的信息、常量、静态变量、即使编译器编译后的代码等信息,在JDK1.8之后,方法区被元空间所替代,并且被挪到了堆外。
在方法区中存在着一个运行时常量池,它用来存储字面量和符号引用。