JVM组成原理(运行时数据区)
JDK、JRE、JVM关系
jdk包含jre,同时还包括一些用来开发java程序使用到的像编译和debug的工具。jre主要是类库,jvm和一些其它运行java程序所使用到的组件。
Description of Java Conceptual DiagramJVM简介
JVM宏观上来说是一套规范,这个规范用来说明实现一个具体的JVM需要符合哪些标准,遵从哪些规范。我们知道java是一次编译到处运行的一种语言。为了达到这种目的,java程序是运行在jvm上,但是我们有不同的操作系统,所以就需要有不同的jvm来和操作系统对应。这些不同的jvm都是遵循一套统一的规范。
JVM组成
JVM的官方文档上,对JVM的结构是这么解释的:
JVM结构这篇文章的主要来说明一下2.5即运行时数据区
运行时数据区
由图可知总共有6个部分组成:
1,PC寄存器,线程私有,一般保存当前正在执行命令的地址。
2,栈,线程私有,用来储存方法帧。
3,堆,线程共享,对象和数组创建时使用。
4,方法区,线程共享,存储类的信息,如运行时常量池,字段,方法和构造方法等代码。
5,运行时常量池,是类或者接口运行时常量池的代表。
6,本地方法栈,线程私有,本地方法的管理栈。
分析
JVM有多种运行时数据区,一些是JVM启动时创建,JVM退出时销毁。另外一些是线程私有的,这些区域是当线程被创建时创建,当线程退出时被销毁。
1,PC寄存器
JVM是支持多线程的。每个线程都有自己的PC寄存器。任何时刻,每个线程都在执行本线程的当前方法。如果这个方法不是本地的,PC寄存器存放了当前JVM正在执行的命令。如果这个方法是本地的,PC寄存器暂时没有定义。PC寄存器的大小足够来存放一个返回值地址或者一个本地方法的指针。
2,栈
每个线程在创建时就会创建自己的栈。栈是用来存储方法帧的。和其它语言的栈相同,java的栈也是在方法调用和返回时用来存储临时变量和中间结果的。因为栈除了进栈和出栈之外不允许直接操作,所以方法帧可以存储在堆里边。栈的内存可以是不连续的。
栈可以是固定大小的,也可以是根据计算动态扩展或压缩。如果是固定大小,每个栈在创建时可以指定其大小。
实现JVM时可以给用户去控制栈的初始大小,和栈的最大和最小值(栈的大小是动态时)。
当需要的空间比栈的最大值还大时,就会出现栈溢出。
当JVM没有足够的内存分配给栈时就会出现内存溢出。
3,堆
堆是线程共享的,是用来给对象和数组创建分配内存的。
JVM一启动,堆就创建了。对象会被GC回收,对象不需要用户自己去显式回收。JVM规范不提供任何自动内存管理系统,实现JVM规范的可以根据自己的系统需要来选择技术。堆可以是固定大小也可以是动态的。堆的内存也可以是不连续的。
实现JVM时可以让用户去设置堆的大小或堆的最大值,最小值(动态时)。
堆内存不够时,会出现内存溢出。
4,方法区
方法区是线程共享的。和其它传统语言相同,它是用来存储一些像运行时常量池,成员变量,方法数据,方法和构造方法的代码等类的结构信息。
方法区在JVM启动时创建。理论上说,它是堆的一部分,但是实现JVM时一般不要让GC来清理这个区域。本规范不指定方法区的具体位置和管理编译后代码的策略。方法区的大小可以是固定的也可以是动态的,内存也可以是不连续的。
实现JVM时可以让用户去设置方法区的大小或方法区的最大值,最小值(动态时)。
如果方法区的空间不够时就会出现内存溢出。
5,运行时常量池
常量池是每一个类文件的运行时常量池的代表,包含很多种常量,像方法的编译时间和成员变量的初始化。和其它编程语言的符号表类似。
常量池是方法区的一部分。当一个类或接口被创建时,这个类或接口的常量池就会被JVM创建。
当常量池内存不够时就会出现内存溢出。
6,本地方法栈
实现JVM时可以使用传统的C栈来支持本地方法,也可以被JVM的对另外一个语言的翻译器来使用。那些不需要执行本地方法或者不依赖于传统的栈的JVM是不需要支持本地方法栈的。如果支持了,本地方法栈通常是线程私有的,线程创建时创建。
本地方法栈可以是固定大小也可以是动态的。如果是固定大小,在创建时大小是可以指定的。
实现JVM时可以让用户去设置本地方法栈的大小或最大值,最小值(动态时)。
当需要的空间比栈的最大值还大时,就会出现栈溢出。
当JVM没有足够的内存分配给栈时就会出现内存溢出。