一、运行时数据区域划分
根据《Java虚拟机规范》的规定,运行时数据区通常包括这几个部分:程序计数器(Program Counter Register)、Java栈(VM Stack)、本地方法栈(Native Method Stack)、方法区(Method Area)、堆(Heap)。
注:有填充色为线程共享区,无填充色为线程隔离的数据区
二.运行时数据区各部分区域存储的数据:
2.1程序计数器
2.1.1 作用:一块较小的存储空间,可以看作是当前线程所执行的字节码行号指示器。字节码解释器工作时就是通过改变这个计数器的值来选取下一条药执行的指令;
2.1.2 线程私有空间:为了线程切换后恢复到正确的位置,确保各计数器间互不影响,独立存储;
2.1.3 存储:指令,如果正在执行一个java方法在,则存储正在执行虚拟机字节码指令的地址;如果执行的是native方法计数器存储的是Undefined
2.1.4 异常类型:无
2.2Java虚拟机栈
2.2.1作用:每个方法执行的时候,Java虚拟机都会同步创建一个栈帧用于存储局部变量表、操作数栈、动态链接、方法出口等信息,每个方法被调用至执行完毕的过程,就对应一个栈帧在虚拟机栈中 从入栈到出栈的过程
2.2.2线程私有空间,生命周期与线程相同
2.2.3存储:局部变量表(Local Variables)、操作数栈(Operand Stack)、指向当前方法所属的类的运行时常量池的引用、方法返回地址(Return Address)和一些额外的附加信息:如下图
局部变量表:就是用来存储方法中声明的变量。对于基本数据类型的变量,则直接存储它的值,对于引用类型的变量,则存的是指向对象的引用及returnAddress(指向了一条字节码指令的地址),存储空间以局部变量槽slot来表示(除long与double占用两个,其余类型均占用一个)。局部变量表的大小在编译器就可以确定其大小了,因此在程序执行期间局部变量表的大小是不会改变的。
操作数栈:栈最典型的一个应用就是用来对表达式求值。
指向运行时常量池的引用:因为在方法执行的过程中有可能需要用到类中的常量,所以必须要有一个引用指向运行时常量。
方法返回地址:当一个方法执行完毕之后,要返回之前调用它的地方,因此在栈帧中必须保存一个方法返回地址。
2.2.4异常:StackOverFlowError:线程请求栈的深度大于大于虚拟允许的深度
OutOfMemoryError:当栈扩展时无法申请到足够的内存时抛出。
2.3本地方法栈
本地方法栈与Java栈的作用和原理非常相似。区别只不过是Java栈是为执行Java方法服务的,而本地方法栈则是为执行本地方法(Native Method)服务的。在JVM规范中,并没有对本地方发展的具体实现方法以及数据结构作强制规定,虚拟机可以自由实现它。在HotSopt虚拟机中直接就把本地方法栈和Java栈合二为一,异常类型同Java虚拟机栈。
2.4Java堆
Java堆是虚拟机管理内存中所占用最大的一块,虚拟机启动时创建,唯一的目就是存放实例对象,Java堆是垃圾收集管理的内存区域,因此也称为GC堆。异常类型 OutOfMemoryError,线程共享。
2.5方法区
方法区域java堆一样,各个线程共享的区域,它用于存储被虚拟机加载的类型信息、常量、静态变量、即时编译器编译后的代码换缓存等数据。到JDK8废弃永久代用元空间代替。运行时常量也是方法区的一部分,用于存放在编译器生成的各种字面量与符号引用,这部分内容将在类加载后存放到方法区的运行时常量池中。
注1:本机内存(Direct Memory)不是虚拟机运行时数据的一部分,但是频繁使用,而且也可能导致出现OutOfMemoryError,因为JDK1.4引入NIO,可以使用Native函数库直接访问堆外内存,堆外内存不受java堆大小限制,但是会受到本机内存大小的限制,所以jvm设置-Xmx时应该预留部分内存,防止出现OutOfMemoryError。
注2:常见的参数如下:
-Xms64m
最小堆内存64m
.-Xmx128m
最大堆内存128m
.-XX:NewSize=30m
新生代初始化大小为30m
.-XX:MaxNewSize=40m
新生代最大大小为40m
.-Xss=256k
线程栈大小。-XX:+PrintHeapAtGC
当发生 GC 时打印内存布局。-XX:+HeapDumpOnOutOfMemoryError
发送内存溢出时 dump 内存。
新生代和老年代的默认比例为 1:2
,也就是说新生代占用 1/3
的堆内存,而老年代占用 2/3
的堆内存。
可以通过参数 -XX:NewRatio=2
来设置老年代/新生代的比例。