美文网首页
你不得不知道的JVM(中)

你不得不知道的JVM(中)

作者: 小驴小驴 | 来源:发表于2021-09-17 00:52 被阅读0次

目录

目录一、运行时数据区(详述)1.1 程序计数器1.2 本地方法栈1.3 虚拟机栈1.3.1 什么是虚拟机栈1.3.2 栈桢1.3.2.1 局部变量表1.3.2.2 操作数栈1.3.2.2.1 栈式指令集架构 VS 寄存器指令集架构1.3.2.3 动态链接1.3.2.4 返回地址1.4 方法区1.4.1 组成部分1.5 堆

一、运行时数据区(详述)

在程序的运行过程中,为了支持程序的运行JVM中定义了各种不同的运行时区域.有些区域是虚拟机启动时便存在,当虚拟机销毁时退出. 有些区域是随着线程的创建而创建,随着线程的结束而销毁。

本章就运行时数据区中的五个部分进行阐述:堆、虚拟机栈、本地方法栈、程序计数器、方法区。

1.1 程序计数器

特点:

  • 线程安全

    每个线程都有自己的程序计数器空间

  • 唯一不会OOM的运行时数据区且不需要垃圾回收

作用:

线程在执行过程中,需要在不同线程之间来回切换。在线程执行过程中,程序计数器需要记录线程的指令地址,当某线程从丢失CPU使用权到再次获得使用权时,为继续执行剩余指令提供依据。

1.2 本地方法栈

在java的运行过程中,我们经常会调用到C的本地方法来完成功能实现. 针对C/C++代码实现的方法(也就是这些native修饰的方法)调用过程.需要有一个本地方法栈来保存方法的执行操作逻辑和操作数据.本地方法栈就是这样的一个角色。

1.3 虚拟机栈

1.3.1 什么是虚拟机栈

虚拟机栈是一种标准的数据结构,当每个线程被创建起之后,都会创建其独立的虚拟机栈空间,每个虚拟机栈都会有独立的虚拟机栈。

当线程需要执行某个方法时,方法以栈桢(Frame)形式压入虚拟机栈中,当方法执行完毕之后再从虚拟机栈中弹出。

这样的一种栈结构非常适合方法之间的调用执行。

1.3.2 栈桢

通过上面的描述栈桢可以理解为线程执行某个方法,方法的运行时空间。栈桢的生命周期随方法的调用而发起,随方法的执行完毕而销毁。

栈桢的数据结构如下:

虚拟机栈中就是由各个栈桢所组成,因此研究栈桢中的结构是非常有必要的。

在栈桢中有包含如下四个部分:

  • 局部变量表

  • 操作数栈

  • 动态链接

  • 方法返回地址

下面对这四个部分进行详述:

1.3.2.1 局部变量表

在运行时常量池中的变量,栈桢中并没有直接使用,运行时常量池中的变量会加载到栈桢中的局部变量表中去操作使用。

这里也有个面试常问的点:

在一个简单的成员方法中,只有两个变量。问?在栈桢中局部变量表中有几个变量?

同样的问题,如果是在静态方法中呢?

文末见答案吧!

1.3.2.2 操作数栈

操作数栈也是一种栈式数据结构,在方法执行过程中,会根据方法中的指令(如:iload、istore、iconst)将局部变量表中的数据加载到操作数栈中,然后根据操作类型指令(iadd...)去除栈顶对应几个元素进行操作。

可以看出,JVM栈桢中使用的就是这种零地址指令,是通过不断的存入、去除操作数栈中栈顶对应几个元素进行操作,这中间过程中并不涉及到任何变量地址。

这种就称为栈式指令集架构,与之对应的还有寄存器指令集架构(Davlik),而Davlik指令集架构中引入了地址

1.3.2.2.1 栈式指令集架构 VS 寄存器指令集架构
  • 可移植性

    栈式指令集架构不需要地址,在大多机器上都适配,可移植性较好;

    寄存器指令集架构有一地址、二地址等,需要寄存器的支持,可移植性不算好。

  • 占用空间大小

    栈式指令集架构占用空间更少

  • 工作效率

    因为栈式指令集架构没有地址,因此数据之间的操作需要借助操作数栈,效率较低;

    寄存器指令集架构中指令包含地址,可以更加高效执行复杂的操作逻辑。

1.3.2.3 动态链接

Class字节码文件中包含常量池部分,常量池中存放的是变量的符号引用方法的符号引用。当类加载器将Class字节码加载到运行时数据区中,这些常量池中的数据会放入到方法区中的运行时常量池中。

而栈桢中含有指向运行时常量池的引用就是动态链接。

1.3.2.4 返回地址

一个方法有两种方式结束运行:

  • 方法执行完毕正常退出

  • 方法异常退出

方法不论以何种方式结束运行,最终都要有一个返回地址。

1.4 方法区

方法区属于堆的逻辑组成部分,但是不同的厂商具体的实现方式不同,哪怕是相同厂商不同的JDK版本可能实现也有所区别。

比如HotSpot虚拟机中在1.7版本之前,方法区是通过Perm Space实现的,在1.8版本之后是通过元空间实现的。

方法区的生命周期同JVM一致,方法区是线程共享的。

1.4.1 组成部分

  • Kclass文件(类结构信息)

    这里也有一个面试常问的点:类的static变量存储在哪?

    答:在JDK1.6 静态变量存储在Kclass末尾,在JDK1.7开始,静态变量存储在Class对象中,即存储在堆中

  • 运行时常量池

    Class字节码文件中包含常量池信息,当Class文件被ClassLoader加载到JVM时,这些常量池信息会被加载到运行时常量池信息中。

    其中StringTable比较特殊,在1.6版本之前,串池存在于运行时常量池中,在1.7之后,StringTable被拿到了堆中

  • ClassLoader

  • JIT即时编译代码

这里描述一下Kclass与对象的Class对象之间的关系:


1.5 堆

背景:堆是JVM中空间占比最大的一部分。生命周期同JVM,且是线程环境下共享;

“几乎”所有的对象都分配在堆中(下文会讲解到TLAB与栈上分配);

堆是垃圾回收关注的重点对象。

堆的组成部分:

  • 老年代

  • 新生代

    • Eden

    • S0

    • S1

其中Eden:S0:S1 = 8:1:1,为何是这样呢,这其实是根据大量实验得出的最好配比。

重要JVM参数:

  • -Xmx:堆最大空间
  • -Xms:堆的初始化空间

TODO

相关文章

网友评论

      本文标题:你不得不知道的JVM(中)

      本文链接:https://www.haomeiwen.com/subject/tpqjgltx.html