from:http://wenku.baidu.com/view/e73e0fafdd3383c4bb4cd25b.html html
JVM执行程序的过程当中,会使用到各类数据区域,这些区域有各自的用途,建立和销毁时间。根据《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个。本地变量表在进入方法时进行分配,当进入一个方法时,这个方法须要在帧中分配多大的本地变量是一件彻底肯定的事情,在方法运行期间不改变本地变量表的大小。 spa
在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异常