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
之后,方法区
被元空间
所替代,并且被挪到了堆外
。
在方法区
中存在着一个运行时常量池
,它用来存储字面量
和符号引用
。