运行时数据区
1.程序计数器:字节码解释器工作时通过改变这个计数器的值来选取下一条需要执行的字节码指令,分支、循环、跳转、异常处理、线程恢复等基础功能都需要依赖这个计数器完成。每条线程都有一个独立的程序计数器,各条线程之间计数器互相不影响,独立储存。如果线程正在执行的是一个Java方法,这个计数器记录的是正在执行的虚拟机字节码指令的地址;如果正在执行的是Native方法*,这个计数器值则为空(Undefined)。程序计数器内没有写入异常。
*Native方法:用其他语言在Java里实现的方法。
2.虚拟机栈:每个方法在执行的同时都会创建一个栈帧用于存储局部变量表、操作数栈、动态链接、方法出口等信息。局部变量表存放了编译器可知的基本数据类型。引用对象和return Address类型。其中long和double占用两个局部变量空间,其余只占用一个。其内存空间分配在编译器内完成,运行期间不会改变局部变量的大小。虚拟机栈内有两个异常:StackOverflowException(线程请求的栈深度大于虚拟机所允许的栈深度)和OutOfMemoryError(虚拟机动态扩展时无法申请到足够的内存)。
3.本地方法栈:与虚拟机栈作用类似,区别是虚拟机栈为虚拟机执行Java方法服务,本地方法栈为虚拟机使用到的Native方法服务。本地方法栈同样会抛出StackOverflowError和OutOfMemoryError异常。
4.Java堆:是被所有线程共享的一块内存区域,在虚拟机启动时创建。目的是为了存放对象实例,几乎所有的对象实例都在这里分配内存。Java堆是垃圾收集器管理的主要区域。Java堆可以处于物理上不连续但逻辑连续的状态,目前主流虚拟机都是按照可扩展来实现,如果在堆中没有内存完成实例分配,并且堆也无法再扩展时,会抛出OutOfMemoryError异常。
5.方法区:是各个线程共享的内存区域,用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码数据。方法区内存可扩展,并且可以选择不实现垃圾收集。同样,方法区无法满足内存分配需求时,会抛出OutOfMemoryError异常。
6.运行时常量池:是方法区的一部分。Class文件中除了有类的版本、字段、方法、接口等描述信息外,还有常量池这一项,用于存放编译期生成的各种字面量和符号引用。这部分内容将在类加载后进入方法区的运行时常量池中存放。除了编译器,在运行期间也可能将新的常量放入池中,这种特性利用多的是String类的intern()方法。同样,运行时常量池无法满足内存分配需求时,会抛出OutOfMemoryError异常。
7.直接内存:并非虚拟机运行时数据区的一部分,也不是虚拟机规范中定义的内存区域。但若这部分内存被频繁使用,也可能出现OutOfMemoryError异常。在JDK1.4中新加入了NIO(New Input/Output)类,引入了一种基于通道与缓冲区的I/O方法,它可以使用Native函数库直接分配堆外内存,然后 通过一个存储在Java堆中的DirectByteBuffer对象作为这块内存的引用进行操作。能够在 一些场景中显著提高性能,避免了在Java堆和Native堆中来回复制数据。