Java虚拟机所管理的内存包括以下⼏个运⾏时数据区:程序计数器(Program Counter Register):
是⼀块较⼩的内存空间,可以看作是当前线程所执⾏的
字节码的⾏号指示器
;字节码解释
器⼯作时就是通过改变这个计数器的值来选取下⼀条需要执⾏的字节码指令。 由于Java
虚拟机的多线程是通过线程轮流切换、分配处理器执⾏时间的⽅式来实现的,在任何⼀个确定的时刻,⼀个处理器(
对于多核处理器来说是⼀个内核
)都只会执⾏⼀条线程中的指令。因此,为了线程切换后能恢复到正确的执⾏位置,每条线程都需要有⼀个独
⽴的程序计数器
,各条线程之间计数器互不影响,独⽴存储,我们称这类内存区域为
“
线
程私有
”的
内存。
如果线程正在执⾏的是⼀个
Java
⽅法,这个计数器记录的是正在执⾏的虚拟机字节码指
令的地址
;
如果正在执⾏的是本地
(Native)
⽅法,这个计数器值则应为空
(Undefined)
。此
内存区域是唯⼀⼀个在《
Java
虚拟机规范》中
没有规定任何
OutOfMemoryError
情况的
区域
。
ava
虚拟机栈
(Java Virtual Machine Stack):
Java
虚拟机栈也是
线程私有
的,它的
⽣命周期与线程同步
。虚拟机栈描述的是
Java
⽅法
执⾏的线程内存模型
:
每个⽅法被执⾏的时候,
Java
虚拟机都会同步创建⼀个
栈帧
(Stack
Frame)
⽤于存储
局部变量表
、
操作数栈
、
动态连接
、
⽅法出⼝
等信
息。每⼀个⽅法被调⽤直⾄执⾏完毕的过程,就对应着⼀个栈帧在虚拟机栈中从
⼊栈到出
栈
的过程。
对这个内存区域规定了两类异常状况
:
如果线程请求的栈深度⼤于虚拟机所允许的深度,
将抛出
StackOverflowError
异常
;
如果
Java
虚拟机栈容量可以动态扩展,当栈扩展时⽆
法申请到⾜够的内存会抛出
OutOfMemoryError
异常
(HotSpot
虚拟机的栈容量是不可以
动态扩展的
)
。
本地⽅法栈
(Native Method Stacks):
本地⽅法栈与虚拟机栈所发挥的作⽤是⾮常相似的,其区别只是
虚拟机栈为虚拟机执⾏
Java
⽅法
(
也就是字节码
)
服务
,⽽本地⽅法栈则是为虚拟机使⽤到的
本地
(Native)
⽅法服
务
。
ava
堆
(Java Heap):
Java
堆是虚拟机所管理的
内存中最⼤的⼀块
。
Java
堆是被
所有线程共享
的⼀块内存区
域,在虚拟机启动时创建。此内存区域的唯⼀⽬的就是
存放对象实例
,
Java
世界⾥
“
⼏
乎
”
所有的对象实例都在这⾥分配内存
。
Java
堆是
垃圾收集器管理
的内存区域,如果从分配内存的⻆度看,所有线程共享的
Java
堆中可以划分出
多个线程私有的分配缓冲区
(Thread Local Allocation Bu
ff
er
,
TLAB)
,
以提升对象分配时的效率不过⽆论从什么⻆度,⽆论如何划分,都不会改变
Java
堆中存
储内容的共性,⽆论是哪个区域,存储的都
只能是对象的实例
。
Java
堆可以处于
物理上不连续的内存空间
中,但在
逻辑上它应该被视为连续的
,这点就
像我们⽤磁盘空间去存储⽂件⼀样,并不要求每个⽂件都连续存放。但对于
⼤对象
(
典型
的如数组对象
)
,多数虚拟机实现出于实现简单、存储⾼效的考虑,很可能会要求连续的
内存空间。
Java
堆既可以被实现成固定⼤⼩的,也可以是可扩展的,不过当前主流的
Java
虚拟机都
是按照可扩展来实现的
(
通过参数
-Xmx
和
-Xms
设定
)
。如果在
Java
堆中没有内存完成实例
分配,并且堆也⽆法再扩展时,
Java
虚拟机将会抛出
OutOfMemoryError
异常。
⽅法区
(Method Area):
⽅法区与
Java
堆⼀样,是各个
线程共享
的内存区域,它⽤于存储已被虚拟机加载的
类型
信息
、
常量
、
静态变量
、即时编译器编译后的代码缓存等数据。虽然《
Java
虚拟机规
范》中把⽅法区描述为
堆的⼀个逻辑部分
,但是它却有⼀个别名叫作
“
⾮堆
”(Non-Heap)
,
⽬的是与
Java
堆区分开来。
运⾏时常量池
(Runtime Constant Pool)
是⽅法区的⼀部分。
Class
⽂件中除了有类的版
本、字段、⽅法、接⼝等描述信息外,还有⼀项信息是
常量池表
(Constant Pool
Table)
,⽤于存放编译期⽣成的各种
字⾯量
与
符号引⽤
,这部分内容将在类加载后存放到
⽅法区的运⾏时常量池中。⼀般来说,除了保存
Class
⽂件中描述的
符号引⽤
外,还会把
由符号引⽤翻译出来的
直接引⽤
也存储在运⾏时常量池中。
运⾏时常量池相对于
Class
⽂件常量池的另外⼀个重要特征是
具备动态性
,
Java
语⾔并不要求常
量⼀定只有编译期才能产⽣,也就是说,并⾮预置⼊
Class
⽂件中常量池的内容才能进⼊⽅法区运
⾏时常量池,运⾏期间也可以将新的常量放⼊池中,这种特性被开发⼈员利⽤得⽐较多的便是
String
类的
intern()
⽅法。
直接内存
(Direct Memory):
直接内存并不是虚拟机运⾏时数据区的⼀部分,也不是《
Java
虚拟机规范》中定义的内
存区域。但是这部分内存也被频繁地使⽤,⽽且也可能导致
OutOfMemoryError
异常出
现,本机直接内存的分配不会受到
Java
堆⼤⼩的限制,但是,既然是内存,则肯定还是
会受到
本机总内存
(
包括物理内存、
SWAP
分区或者分⻚⽂件
)
⼤⼩以及处理器寻址空间的
限制,⼀般服务器管理员配置虚拟机参数时,会根据实际内存去设置
-Xmx
等参数信息,
但经常忽略掉直接内存,使得各个内存区域总和⼤于物理内存限制
(
包括物理的和操作系
统级的限制
)
,从⽽导致动态扩展时出现
OutOfMemoryError
异常。