java虚拟机所管理的内存将会包括下列几个运行时数据区域:
一、方法区:
1、用于存储已被虚拟机加载的类信息、常量、静态变量和即时编译器编译后的代码等数据。
2、垃圾收集在这个区域较少出现,主要是针对常量池的回收和类型的卸载,当方法区无法满足内存分配需求时,将抛出OutOfMemoryError异常(OOM)。
3、该区域被线程共享。
4、运行时常量池是方法区的一部分,用于存放编译时期生成的各种字面量和符号引用(翻译出来的直接引用也存储在常量池),该常量池具有动态性,也就是说常量并不一定是编译时确定,运行时生成的常量也会存在这个常量池中。
关于字面量、符号引用和直接引用:
字面量相当于Java语言层面常量的概念,如文本字符串,声明为final的常量值等。
符号引用则属于编译原理方面的概念,包括了如下三种类型的常量:1、类和接口的全限定名 2、字段名称和描述符 3、方法名称和描述符。
直接引用可以是直接指向引用目标的指针、相对偏移量或者是一个能够间接定位到目标的句柄。直接引用是和虚拟机的内存布局有关的,同一个符号引用在不同的虚拟机上翻译的直接引用一般是不同的。如果有了直接引用,那么引用的目标必定是存在内存中的。
二、虚拟机栈:
1、虚拟机栈也就是我们平常所称的栈内存,它为java方法服务,每个方法在执行的时候都会创建一个栈帧,用于存储局部变量表、操作数栈、动态链接和方法出口等信息。
2、虚拟机栈是线程私有的,它的生命周期与线程相同。
3、局部变量表存放了编译期可知的基本数据类型、对象引用(不等同于对象本身,可能是一个指向对象起始地址的引用指针,也可能是指向一个代表对象的句柄或其他与此对象相关的位置)和returnAddress类型(指向一条字节码指令的地址)。在32位机器中,Integer要占用一个reference(8 bytes) + 一个int(4字节), 而int则只占4字节自身的空间。在局部变量表中,8 bytes的long和double会占据2个局部变量空间(slot),其他类型只占据一个。
4、操作数栈的作用主要用来存储运算结果以及运算的操作数,它不同于局部变量表通过索引来访问,而是压栈和出栈的方式。
5、每个栈帧都包含一个指向运行时常量池中该栈帧所属方法的引用,持有这个引用是为了支持方法调用过程中的动态链接,动态链接就是将常量池中的符号引用在运行期转化为直接引用。
6、如果虚拟机栈为固定长度,线程请求的栈深度大于虚拟机所允许的深度,将抛出*Error异常;如果虚拟机栈可以动态扩展,扩展时无法申请到足够的内存,就会抛出OutOfMemoryError异常。
三、本地方法栈:
本地方法栈和虚拟机栈所发挥的作用非常相似,它们之间的区别不过是虚拟机栈为虚拟机执行java方法服务,而本地方法栈为虚拟机使用到的native方法服务。
四、堆:
1、此内存区域的唯一目的就是存放对象实例,几乎所有的对象实例都要在这里分配内存。
2、堆是垃圾收集器管理的主要区域。
3、在堆中没有内存完成实例分配,并且堆也无法再扩展时,将抛出OutOfMemoryError异常。
五、程序计数器:
1、内存空间小,可以看成是当前线程所执行的字节码的行号指示器。
2、字节码解释器工作时通过改变这个计数值可以选取下一条需要执行的字节码指令,分支、循环、跳转、异常处理和线程恢复等功能都需要依赖这个计数器完成。
3、为了线程切换后能恢复到正确的执行位置,每条线程都需要有一个独立的程序计数器,各条线程之间计数器互不影响,独立存储。
4、该内存区域是唯一一个java虚拟机规范没有规定任何OutOfMemoryError情况的区域。