VM
运行时数据区域
JVM
执行
Java
程序的过程中,会使用到各种数据区域,这些区域有各自的用途、创建和销毁时间。根据《
Java
虚拟机规范(第二版)》(下文称
VM Spec
)的规定,
JVM
包括下列几个运行时数据区域:
1.
程序计数器(
Program Counter Register
):
每一个
Java
线程都有一个程序计数器来用于保存程序执行到当前方法的哪一个指令,对于非
Native
方法,这个区域记录的是正在执行的
VM
原语的地址,如果正在执行的是
Natvie
方法,这个区域则为空(
undefined
)。此内存区域是唯一一个在
VM Spec
中没有规定任何
OutOfMemoryError
情况的区域。
2.
Java
虚拟机栈(
Java
Virtual Machine Stacks
)
与程序计数器一样,
VM
栈的生命周期也是与线程相同。
VM
栈描述的是
Java
方法调用的内存模型:每个方法被执行的时候,都会同时创建一个帧(
Frame
)用于存储本地变量表、操作栈、动态链接、方法出入口等信息。每一个方法的调用至完成,就意味着一个帧在
VM
栈中的入栈至出栈的过程。在后文中,我们将着重讨论
VM
栈中本地变量表部分。
经常有人把
Java
内存简单的区分为堆内存(
Heap
)和栈内存(
Stack
),实际中的区域远比这种观点复杂,这样划分只是说明与变量定义密切相关的内存区域是这两块。其中所指的“堆”后面会专门描述,而所指的“栈”就是
VM
栈中各个帧的本地变量表部分。本地变量表存放了编译期可知的各种标量类型(
boolean
、
byte
、
char
、
short
、
int
、
float
、
long
、
double
)、对象引用(不是对象本身,仅仅是一个引用指针)、方法返回地址等。其中
long
和
double
会占用
2
个本地变量空间(
32bit
),其余占用
1
个。本地变量表在进入方法时进行分配,当进入一个方法时,这个方法需要在帧中分配多大的本地变量是一件完全确定的事情,在方法运行期间不改变本地变量表的大小。
在
VM Spec
中对这个区域规定了
2
中异常状况:如果线程请求的栈深度大于虚拟机所允许的深度,将抛出
StackOverflowError
异常;如果
VM
栈可以动态扩展(
VM Spec
中允许固定长度的
VM
栈),当扩展时无法申请到足够内存则抛出
OutOfMemoryError
异常。
3.
本地方法栈(
Native Method Stacks
)
本地方法栈与
VM
栈所发挥作用是类似的,只不过
VM
栈为虚拟机运行
VM
原语服务,而本地方法栈是为虚拟机使用到的
Native
方法服务。它的实现的语言、方式与结构并没有强制规定,甚至有的虚拟机(譬如
Sun
Hotspot
虚拟机)直接就把本地方法栈和
VM
栈合二为一。和
VM
栈一样,这个区域也会抛出
StackOverflowError
和
OutOfMemoryError
异常。
4.Java
堆(
Java Heap
)
对于绝大多数应用来说,
Java
堆是虚拟机管理最大的一块内存。
Java
堆是被所有线程共享的,在虚拟机启动时创建。
Java
堆的唯一目的就是存放对象实例,绝大部分的对象实例都在这里分配。这一点在
VM
Spec
中的描述是:所有的实例以及数组都在堆上分配(原文:
The heap is the runtime data area from which memory for all class
instances and arrays is allocated
),但是在逃逸分析和标量替换优化技术出现后,
VM Spec
的描述就显得并不那么准确了。
Java
堆内还有更细致的划分:新生代、老年代,再细致一点的:
eden
、
from survivor
、
to survivor
,甚至更细粒度的本地线程分配缓冲(
TLAB
)等,无论对
Java
堆如何划分,目的都是为了更好的回收内存,或者更快的分配内存,在本章中我们仅仅针对内存区域的作用进行讨论,
Java
堆中的上述各个区域的细节,可参见本文第二章《
JVM
内存管理:深入垃圾收集器与内存分配策略》。
根据
VM Spec
的要求,
Java
堆可以处于物理上不连续的内存空间,它逻辑上是连续的即可,就像我们的磁盘空间一样。实现时可以选择实现成固定大小的,也可以是可扩展的,不过当前所有商业的虚拟机都是按照可扩展来实现的(通过
-Xmx
和
-Xms
控制)。如果在堆中无法分配内存,并且堆也无法再扩展时,将会抛出
OutOfMemoryError
异常。
5.
方法区(
Method Area
)
叫“方法区”可能认识它的人还不太多,如果叫永久代(
Permanent
Generation
)它的粉丝也许就多了。它还有个别名叫做
Non-Heap
(非堆),但是
VM Spec
上则描述方法区为堆的一个逻辑部分(原文:
the method area is logically part of the heap
),这个名字的问题还真容易令人产生误解,我们在这里就不纠结了。
方法区中存放了每个
Class
的结构信息,包括常量池、字段描述、方法描述等等。
VM Space
描述中对这个区域的限制非常宽松,除了和
Java
堆一样不需要连续的内存,也可以选择固定大小或者可扩展外,甚至可以选择不实现垃圾收集。相对来说,垃圾收集行为在这个区域是相对比较少发生的,但并不是某些描述那样永久代不会发生
GC
(至少对当前主流的商业
JVM
实现来说是如此),这里的
GC
主要是对常量池的回收和对类的卸载,虽然回收的“成绩”一般也比较差强人意,尤其是类卸载,条件相当苛刻。
6.
运行时常量池(
Runtime Constant Pool
)
Class
文件中除了有类的版本、字段、方法、接口等描述等信息外,还有一项信息是常量表
(constant_pool table)
,用于存放编译期已可知的常量,这部分内容将在类加载后进入方法区(永久代)存放。但是
Java
语言并不要求常量一定只有编译期预置入
Class
的常量表的内容才能进入方法区常量池,运行期间也可将新内容放入常量池(最典型的
String.intern()
方法)。
运行时常量池是方法区的一部分,自然受到方法区内存的限制,当常量池无法在申请到内存时会抛出
OutOfMemoryError
异常。
7.
本机直接内存(
Direct Memory
)
直接内存并不是虚拟机运行时数据区的一部分,它根本就是本机内存而不是
VM
直接管理的区域。但是这部分内存也会导致
OutOfMemoryError
异常出现,因此我们放到这里一起描述。
在
JDK1.4
中新加入了
NIO
类,引入一种基于渠道与缓冲区的
I/O
方式,它可以通过本机
Native
函数库直接分配本机内存,然后通过一个存储在
Java
堆里面的
DirectByteBuffer
对象作为这块内存的引用进行操作。这样能在一些场景中显著提高性能,因为避免了在
Java
对和本机堆中来回复制数据。
显然本机直接内存的分配不会受到
Java
堆大小的限制,但是即然是内存那肯定还是要受到本机物理内存(包括
SWAP
区或者
Windows
虚拟内存)的限制的,一般服务器管理员配置
JVM
参数时,会根据实际内存设置
-Xmx
等参数信息,但经常忽略掉直接内存,使得各个内存区域总和大于物理内存限制(包括物理的和操作系统级的限制),而导致动态扩展时出现
OutOfMemoryError
异常。
原文出自:http://www.iteye.com/topic/802573
分享到:
相关推荐
第3节: 揭秘JVM运行时数据区-02第3节: 揭秘JVM运行时数据区-02第3节: 揭秘JVM运行时数据区-02第3节: 揭秘JVM运行时数据区-02第3节: 揭秘JVM运行时数据区-02第3节: 揭秘JVM运行时数据区-02第3节: 揭秘JVM运行...
JVM运行机制深入分析,适用于研究java开发的人员,想深入理解jvm
"JVM内存区域划分" JVM内存区域划分是Java虚拟机(JVM)中的一种内存管理机制,主要分为五个部分:程序计数器、Java栈、本地方法栈、堆和方法区。这些区域各自扮演着不同的角色,共同组成了JVM的内存管理系统。 ...
JVM性能调优_JVM内存区域划分 JVM(Java Virtual Machine)是 Java 语言的核心组件之一,其性能优化对 Java 应用程序的执行效率和稳定性具有至关重要的影响。在 JVM 中,内存区域的划分和管理是性能优化的关键所在...
### JVM运行时数据区详解 #### 一、概述 Java虚拟机(JVM)作为Java程序的运行环境,定义了一系列用于程序执行过程中使用的数据区域。这些数据区域在JVM启动时创建,在JVM退出时销毁。其中一些数据区域是线程独立...
- JVM允许在运行时动态加载类和资源,适应不同环境的需求。 总结来说,JVM的运行机制是Java平台无关性的关键,它通过字节码和解释器实现了代码的跨平台执行,同时通过垃圾回收和内存管理提供了高效且安全的运行...
在Java的学习过程中,我们不可避免地需要了解JVM(Java虚拟机)的工作原理,尤其是内存区域的划分。JVM的内存区域主要分为以下几个部分:程序计数器、Java虚拟机栈、本地方法栈、堆以及方法区。这些部分共同协作,...
### JVM 运行时数据区域、垃圾回收机制与类加载机制详解 #### 一、运行时数据区域 Java虚拟机(JVM)的核心组件之一便是其运行时数据区域,这一区域负责存储程序运行过程中产生的各种数据。为了更好地理解这部分内容...
Java虚拟机(JVM)内存区域的划分是Java性能优化和故障排查的关键所在。这些区域主要分为以下几个部分: 1. **程序计数器(PC,Program Counter Register)**:每个线程都有自己的程序计数器,它记录了当前线程正在...
在JVM中,内存区域被划分为几个不同的部分,每一个部分有着不同的用途,从而实现了JVM的稳定和高效运行。根据上述内容,我们可以详细介绍如下几个主要的内存区域: 1. 程序计数器(Program Counter Register) 程序...
2. **内存区域划分**:JVM内存分为堆内存和栈内存,其中栈内存又包括方法区、虚拟机栈、本地方法栈。堆内存用于存储对象实例,而栈内存则存储线程私有的局部变量、方法参数和运算结果。方法区存储类的信息,如常量池...
"JVM运行时数据区划分原理详解" JVM运行时数据区划分原理详解是Java虚拟机(JVM)的核心组件之一,负责管理Java应用程序的内存资源。该原理详解了JVM如何划分和管理内存空间,以便更好地支持Java应用程序的运行。 ...
然而,当处理的数据量过大或者配置不当,JVM可能会遇到运行时内存溢出的问题。本文将深入探讨这个问题,并提供有效的解决方案。 内存溢出,也称为“Out of Memory”错误,通常是由于程序请求的内存超过了系统或JVM...
常量池静态常量池即*.class文件中的常量池,用于存放字面量和符号引用运行时常量池是jvm运行期间,存储常量的数据结构运行时常量池概念运行时常量池(Runti
JVM的内容分享,包含JVM的优化目标、优化原则、JVM组成、内存区域划分、垃圾回收算法、垃圾回收器、FullGC触发时机、对象布局、元空间存储、GC调优
2. 运行时数据区:JVM将内存分为不同的区域,如方法区(存储类信息)、堆(存放对象实例)、栈(存储方法调用)、程序计数器(记录下一条要执行的指令地址)和本地方法栈(服务本地方法,如JNI调用)。 3. 执行引擎...
当我们谈论"一个JVM启动多个SpringBoot服务"时,实际上是在讨论如何在一个Java进程中运行多个独立的应用实例。 一、SpringBoot服务的独立性 SpringBoot服务通常是一个独立的可执行jar文件,包含所有的依赖库,可以...
SAP JVM 4.1 64位是一个专为SAP系统设计的Java虚拟机,它主要用于运行SAP的应用程序和服务。此版本是为64位操作系统优化的,旨在提供更好的性能和内存管理能力,特别是在处理大数据量和复杂计算场景时。 首先,我们...
JVM运行时数据区是Java虚拟机在运行时对该Java进程占用的内存进行的一种逻辑上的划分,包括方法区、堆内存、虚拟机栈、本地方法栈、程序计数器。这些区块实际都是Java进程在Java虚拟机的运作下通过不同数据结构来对...