1.程序计数器java
为了线程切换后能恢复到正确的执行位置,每条线程都须要有一个独立的程序计数器。若是线程正在执行的是一个java方法,这个计数器记录的是正在执行的虚拟机字节码指令的地址;若是正在执行的是Native方法,这个计数器值则为空(Undenifined)。函数
此内存区域是惟一一个在java虚拟机规范中没有规定任何OutOfMemoryError状况的区域。性能
2.java虚拟机栈线程
也是线程私有的,生命周期与线程相同。描述的是java方法执行的内存模型:无法方法在执行的同时都会建立一个栈帧(Stack Frame)用于存储局部变量、操做数栈、动态连接、方法出口等信息。每一个方法从调用直至执行完成的过程,就对应着一个栈帧在虚拟机栈中入站到出站的过程。设计
java虚拟机规范中,对这个区域规定了两种异常情况:若是线程请求的栈深度大于虚拟机所容许的深度,将抛出StackOverflowError异常;若是虚拟机展能够动态扩展(当前大部分的java虚拟机均可动态扩展,只不过java虚拟机规范中也容许固定长度的虚拟机栈),若是扩展是没法申请到足够的内存,就会抛出OutOfMemoryError异常。对象
3.本地方法栈接口
为虚拟机使用到的Native方法服务。有的虚拟机(譬如Sun HotSpot虚拟机)直接就把本地方法栈和虚拟机栈合二为一。生命周期
与虚拟机栈同样,本地方法栈也会抛出StackOverflowError和OutOfMemoryError异常。进程
4.java堆内存
根据java虚拟机规范的规定,java堆能够处于物理上不连续的内存空间中,只要逻辑上是连续的便可。首先是,既能够固定大小也能够是可扩展的,当前主流虚拟机都是可扩展的。
若是堆中没有内存完成实例分配,而且堆也没法在扩展时,将会抛出OutOfMemoryError异常。
①方法区
用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。虽然java虚拟机规范把方法区描述为对的一个逻辑部分,可是它却有一个别名叫作Non-Heap(非堆),目的应该是与java堆区区分开来。
对于习惯在HotSpot虚拟机上开发、部署程序的开发者来讲,不少人都更愿意把方法区称为“永久代”(Permanent Generation),本质上二者并不等价,仅仅是由于HotSpot虚拟机的设计团队选择吧GC分代收集扩展至方法区,或者说使用永久代来实现方法区而已,这样HotSpot的垃圾收集器能够像管理Java对同样管理这部份内存,省去专门为方法区编写内存管理的代码的工做。其余虚拟机(BEA JRockit、IBM J9)来讲是不存在永久代的概念的。
原则上, 如何实现方法区属于虚拟机实现细节, 不受虚拟机规范约束, 但使用永久代来实现方法区, 如今看来并非一个好主意, 由于这样更容易遇到内存溢出问题( 永久代有-XX: MaxPermSize的上限, J9和JRockit只要没有触碰到进程可用内存的上限, 例如32位系统中的4GB, 就不会出现问题) , 并且有极少数方法( 例如String.intern( ) ) 会因这个缘由致使不一样虚拟机下有不一样的表现。 所以, 对于HotSpot虚拟机, 根据官方发布的路线图信息, 如今也有放弃永久代并逐步改成采用Native Memory来实现方法区的规划了[1], 在目前已经发布的JDK 1.7的HotSpot中, 已经把本来放在永久代的字符串常量池移出。
Java虚拟机规范对方法区的限制很是宽松, 除了和Java堆同样不须要连续的内存和能够选择固定大小或者可扩展外, 还能够选择不实现垃圾收集。 相对而言, 垃圾收集行为在这个区域是比较少出现的, 但并不是数据进入了方法区就如永久代的名字同样“ 永久” 存在了。 这区域的内存回收目标主要是针对常量池的回收和对类型的卸载, 通常来讲, 这个区域的回收“ 成绩” 比较难以使人满意, 尤为是类型的卸载, 条件至关苛刻, 可是这部分区域的回收确实是必要的。 在Sun公司的BUG列表中, 曾出现过的若干个严重的BUG就是因为低版本的HotSpot虚拟机对此区域未彻底回收而致使内存泄漏。根据Java虚拟机规范的规定, 当方法区没法知足内存分配需求时, 将抛出OutOfMemoryError异常。
②运行时常量池
是方法区的一部分。Class文件中除了有类的版本、字段、方法、接口等描述信息外,还有一项信息是常量池(Constant Pool Table),用于存放编译期生成的各类字面量和符号引用,这部份内容将在类加载后进入方法去的运行时常量池中存放。
运行时常量池相对于CLass文件常量池的另一个重要特征是具有动态性,Java语言并不要求常量必定只有编译期才能产生,也就是并不是预置入CLass文件中常量池的内容才能进入方法区运行时常量池,运行期间也可能将新的常量放入池中,这种特性被开发人员利用比较多的就是String类的intern()方法。
既然运行时常量池是方法区的一部分,天然受到方法区内存的限制,当常量池没法再申请到内存是会抛出OutOfMemoryError异常。
③直接内存
不是虚拟机运行时数据区的一部分,也不是java虚拟机规范中定义的内存区域。
jdk1.4中新加入了NIO(New Input/Output)类,引入了一种基于通道(Channel)与缓冲区(Buffer)的I/O方式,它可使用Native函数库直接分配堆外内存,而后经过一个存储在java堆中的DirectByteBuffer对象做为这块内存的引用进行操做。因避免了在java堆和native堆来回复制数据,显著提升了性能。
动态扩展时,各内存区域总和大于物理内存限制会出现OutOfMemoryError异常。