Java虚拟机(JVM)内存区域
JVM(Java Virtual Machine)内存区域由五部分组成:
程序计数器、java虚拟机栈、本地方法栈
堆、方法区
程序计数器
程序计数器(Program Counter Register)是一块较小的内存空间,它可以看做当前线程所执行的字节码的行号指示器。
字节码解释器通过改变程序计数器的值来选取下一条需要执行的字节码指令,分支,循环,跳转,异常处理等基本功能都需要依赖程序计数器完成。
线程私有,在多线程情况下,程序计数器能使线程切换后恢复正确的执行位置。
当线程执行的是一个java方法时,这个计数器记录的是该条字节码指令的地址。
当线程执行的是一个本地方法时,这个计数器的值为空。
程序计数器的内存区域是唯一一个在java虚拟机规范没有规定任何OutofMemroyError情况的区域。
Java虚拟机栈
Java虚拟机栈描述的是Java方法运行过程的内存模型,每一个方法被执行的同时都会创建栈帧用于存储局部变量表、操作数栈、动态链接、方法出口等信息。
每一个方法从调用到执行完的过程就对应一个栈帧在java虚拟机栈中从入栈到出栈的过程。
线程私有,生命周期与线程相同。
在Java虚拟机规范中,对这个区域规定了两种异常情况:
1)如果线程请求的栈深度大于虚拟机所允许的栈深度,将抛出*Error异常。
2)如果虚拟机可以动态扩展(当前大部分虚拟机都可以动态扩展,只不过java虚拟机栈中也允许固定的虚拟机栈),如果扩展时无法申请到足够内存,就会抛出OutOfMemoryError异常。
本地方法栈
本地方法栈与虚拟机栈的作用类似,他们之间区别在于:虚拟机栈为虚拟机执行java方法(也就是字节码)服务,而本地方法栈则为虚拟机使用Native方法服务。
本地方法栈被执行的时候,也会创建栈帧来存储本地方法的局部变量表,操作数栈,动态链接,方法出口等信息。
堆
Java堆是被所有线程共享的一块区域,在虚拟机启动时创建。
几乎所有对象实例以及数组都要在堆上分配。
java堆是垃圾收集器的主要管理区域。按分代收集算法可分为新生代和老年代,再细致一点有Eden空间、From Survivor空间、To Survivor空间等。
java堆可以处于物理上不连续的区域,只要逻辑连续即可。
如果在堆中没有内存完成实例分配,并且也无法再扩展时,将会抛出OutOfMemoryError异常。
方法区
线程共享的内存区域。
用于存储已被虚拟机加载的类信息、常量、静态变量、及时编译器编译后的代码等数据。
Java虚拟机规范把方法区描述为堆的一个逻辑部分。
方法区的内存回收目标主要针对常量池的回收和对类型的卸载。
运行常量池是方法区的一部分。
Class文件中除了有类的版本、字段、方法、接口等描述信息外,还有一项信息是常量池,用于存放编译期生成的各种自变量和符号引用。