1.JVM虚拟机运行时内存区域
jvm运行时所管理的内存将会分为如下几个区域:程序计数器、虚拟机栈、本地方法栈、方法区、堆区。其中,方法区和堆区由所有线程共享,程序计数器、虚拟机栈、本地方法栈将根据线程进行隔离。
1.1 程序计数器
程序计数器(Program Counter Regester):是一块较小的内存空间,作为当前线程锁执行字节码的行号指示器。为当前线程私有。如果执行的是一个java方法,则这个计数器是执行字节码指令的地址;如果执行的是Native方法,则计数器为空值(Undefined)。
该区域是在java虚拟机规范中唯一一个没有规定任何OutOfMemmoryError情况的区域。
1.2 java虚拟机栈
java虚拟机栈(java Virtual Machine Stacks)也是线程私有,生命周期与线程相同。由栈帧(Stack Frame)组成,栈帧包括:局部变量表、操作数栈、动态链接、方法出口信息等。每一个方法从调用至执行完成的过程,都对应着一个栈帧在虚拟机栈中的入栈到出栈的过程。
局部变量表存放了编译期可知的各种基本数据类型(boolean、byte、char、short、int、float、long、double)和对象引用。其中64位长度的long、double会占用2个局部变量空间(Slot)。其余的占一个空间。
该区域会出现两种异常:StackOverflewError:栈深度大于虚拟机栈所允许的深度;OutOfMemmoryError:栈扩展时无法申请到足够的内存。
1.3 本地方法栈
本地方法栈(Native method Stack)与虚拟机栈类似,区别在于本地方法栈为Native方法服务。在HotSpot虚拟机中,直接把虚拟机方法栈和本地方法栈合二为一。与虚拟机方法栈类似,该区域也会出现StackOverflewError和OutOfMemmoryError异常。
1.4 Java堆
Java堆(JAVA Heap)是java虚拟机所管理内存中最大的一块,在虚拟机启动时创建,用于存放对象实例,几乎所有的对象都在此分配内存。(并不绝对,JIT编译器发展和逃逸分析技术逐渐成熟,支持在栈上分配)。
java堆可细分为:新生代和老年代;其中新生代又包括 Eden空间,From Survivor空间和To Survivor空间。
java堆通过 -Xms 和-Xmx配置堆的大小。如果堆无法再扩展,将会抛出OutOfMemoryError异常。
1.5 java方法区
java 方法区(Method Area)与java堆一样,是各个线程共享的内存区域,用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。通常用 Non-Heap(非堆)来做别称与java堆区分开。在HotSpot虚拟机中,也称为永久代(Permanent Generation),在本质上二者并不等价,仅仅是HotSpot虚拟机把GC分代收集扩展至方法区,或者说是用永久代来实现方法区。
当方法区无法满足内存分配时,将会抛出OutOfMemoryError异常。
1.6 运行时常量池
运行时常量池(Runtime Constant Pool)是方法区的一部分。用于存放class文件中的各种字面量和符号引用。 运行时常量池对于class文件常量池的另外一个重要特征就是具有动态性,在运行期间,可以将新的产量放入该常量池。如 String 的intern()方法。
当常量池无法再申请到内存时会抛出OutOfMemoryError异常。
1.7 直接内存
直接内存(Direct Memory) 并不是java虚拟机运行时内存的一部分。在jdk1.4之后的版本中加入了NIO,引入一种基于通道channel和缓冲区Buffer的IO方式,可以直接使用分配堆外内存。然后通过存储在堆中的DirectByteBuffer对象作为这块内存的引用来操作。能显著提高性能,避免在java堆和native堆中来回复制数据。 当该区域和哥哥内存区域总和大于物理内存限制的时候,会出现OutOfMemoryError异常。