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