Java运行时内存区域

Java虚拟机(Java SE 7版)管理的内存包括以下几个运行时数据区域。

Java运行时内存区域_第1张图片


1. 程序计数器

程序计数器Program Counter Register)是当前线程所执行的字节码的行号指示器。字节码解释器需要通过这个计数器的值来读取下一条执行的字节码指令,各种分支跳转、异常处理等。

Java虚拟机的多线程是通过线程轮流切换并且分配时间片的方式来实现的,所以在任何一个moment,一个处理器都只会执行一条线程中的指令。因为为了线程切换后能够恢复到正确的执行位置,每个线程都需要一程序计数器,所以这一部分计数器的内存区域为“线程私有”的。

如果线程正在执行的是一个Java method,这个计数器记录的是虚拟机字节码指令的地址;如果正在执行的是native method,这个计数器值为 Undefined。


2. 虚拟机栈

虚拟机栈Visual Machine Stacks)描述的是Java方法执行的内存模型,它也是线程私有的。每个Java method在执行的时候都会创建一个 stack frame用来存储局部变量表、操作数栈、方法出口等信息,每个Java method的执行过程都对应着一个stack frame从入栈到出栈的过程。

局部变量表(local variables)
局部变量表用于存放编译期的各种基本数据类型和对象引用,如int、long、float、double、reference和returnType。各种类型的数据所需要的空间大小在编译期间已经确定,对于byte、short以及char类型的值会被转换为int,以对齐一个局部变量空间(Slot)。所以一个方法在stack frame中需要分配的局部变量空间大小是确定的。
操作数栈(operand stack)
和局部变量表一样,操作数栈也是被组织成一个以字长为单位的数组。但是和前者不同的是,它不是通过索引来访问,而是通过标准的栈操作—压栈和出栈—来访问的。不同于程序计数器,Java虚拟机没有寄存器,程序计数器也无法被程序指令直接访问。Java虚拟机的指令是从操作数栈中而不是从寄存器中取得操作数的,因此它的运行方式是基于栈的而不是基于寄存器的。

3. 本地方法栈

本地方法栈Native Method Stack)和虚拟机栈的作用非常相似,他们的区别不过是虚拟机栈为Java method服务,而本地方法栈为Native method服务。与虚拟机一样,该内存区域可以动态拓展,不过如果线程请求的栈深度大于虚拟机所允许的深度,就会抛出StackOverflowError异常,所申请的内存无法满足时也会抛出OutOfMemoryError异常。


4. Java堆

Java Heap)是Java虚拟机管理的内存中最大的一块,它是被所有线程共享的一块内存区域,在虚拟机启动的时候创建。此内存区域的目的是存放对象实例,几乎所有的对象实例以及数组都要在堆上分配。这一点在Java虚拟机规范中是这样描述它的。

The heap is the runtime data area from which memory for all class instances and arrays is allocated.
Java堆也被称为“ GC堆”( Garbage Collected Heap)。堆中根据对象的存活时间还可以分为Eden区,From Survivor区,To Survivor区等。从内存分配的角度看,线程共享的Java堆中可能划分出多个线程私有的分配缓冲区(Thread Local Allocation Buffer, TLAB),但是无论如何划分,存储的仍然是对象实例,进一步划分的目的是为了更好地回收内存,或者更合理地分配内存。
Java虚拟机规范中提到,Java堆可以处于物理上不连续的内存空间中,只要逻辑上连续即可,就像我们的磁盘空间一样,既可以实现成固定大小的,也可以是可扩展的,当前主流的虚拟机都是可配置的(通过-Xmx和-Xms设置)。如果堆中没有足够的内存可以完成实例分配时,而且堆也无法再扩展时就会抛出OutOfMemoryError囖。

5. 方法区

方法区Method Area)与Java堆一样,是各个线程共享的内存区域,用于存储被虚拟机加载的类信息、常量、静态变量、编译后的代码等数据。Java虚拟机规范把方法去描述为堆的一个逻辑部分,但它却又被称为Non-Heap,目的是与Java堆区分开来。

方法区和Java堆一样不需要连续的内存和可以配置固定大小和可拓展外,还可以配置不实现垃圾回收。相对而言,垃圾收集行为在这个内存区域非常少出现,但并非真正的“永久代”(Permanent Generation),这块区域的回收主要是针对常量池的回收和对类型的卸载。同样地,当方法区无法满足内存分配的需求时也将抛出OutofMemoryError异常。

运行时常量池(Runtime Constant Pool)
运行时常量池是方法区的一部分。用于存放编译器生成的各种字面量和符号引用,这部分内容将在类加载后进入方法区的运行时常量池中存放。
运行时常量池具备动态性,常量不一定只有编译器才会产生,也就是并非Class文件中常量池的内容才能进入方法区运行时常量池,运行期间也能将新的常量放入池中,例如String类的intern方法。

到这里我们大致了解Java虚拟机的各个数据区域的作用了,至于这些数据区域的其他细节,包括如何创建、访问、回收的,以后再讨论。

你可能感兴趣的:(Java,java,jvm,heap,stack,内存)