Java虚拟机(JVM)的工作方式

  • 一叠
  • 执行环境
  • 垃圾收集堆
  • 恒定池
  • 方法存储区
  • 指令集

寄存器

寄存器 Java虚拟机的类似于在我们的电脑寄存器。但是,由于虚拟机是基于堆栈的,因此其寄存器不用于传递或接收参数。在Java中,寄存器保存机器的状态,并在执行每一行字节代码后进行更新以保持该状态。以下四个寄存器保存虚拟机的状态:

  • frame,参考框架,并包含指向当前方法的执行环境的指针。
  • optop,操作数顶部,并包含一个指向操作数堆栈顶部的指针,用于评估算术表达式。
  • pc是程序计数器,包含要执行的下一个字节代码的地址。
  • vars,变量寄存器,并包含一个指向局部变量的指针。

所有这些寄存器均为32位宽,并立即分配。这是可能的,因为编译器知道局部变量和操作数堆栈的大小,并且解释器知道执行环境的大小。

堆栈

Java虚拟机使用操作数堆栈为方法和操作提供参数,并从方法和操作接收结果。所有字节码指令均从堆栈中获取操作数,对其进行操作,然后将结果返回至堆栈。像虚拟机中的寄存器一样,操作数堆栈为32位宽。

操作数堆栈遵循后进先出(LIFO)方法,并期望堆栈上的操作数具有特定顺序。例如,isub字节码指令期望将两个整数存储在堆栈的顶部,这意味着操作数必须已被上一组指令压入了那里。isub将操作数从堆栈中弹出,相减,然后将结果推回堆栈。

在Java中,整数是原始数据类型。每个原始数据类型都有唯一的指令,该指令告诉它如何对该类型的操作数进行操作。例如,lsub字节码用于执行长整数减法,fsub字节码用于执行浮点减法,dsub字节码用于执行长整数减法。因此,将两个整数压入堆栈,然后将它们视为单个长整数是非法的。但是,将64位长的整数压入堆栈并占用两个32位插槽是合法的。

Java程序中的每个方法都有一个与之关联的堆栈框架。的堆栈帧保存与三组数据的方法的状态:该方法的局部变量,所述方法的执行环境,并且该方法的操作数栈。尽管局部变量和执行环境数据集的大小总是在方法调用开始时固定的,但是操作数堆栈的大小会随着执行方法的字节码指令而改变。由于Java堆栈宽32位,因此不能保证64位数字是64位对齐的。

执行环境

所述执行环境保持堆栈作为数据集之内,并且是用来处理动态链接,正常方法返回,和异常产生。为了处理动态链接,执行环境包含对当前方法和当前类的方法和变量的符号引用。通过动态链接到符号表将这些符号调用转换为实际的方法调用。

只要方法正常完成,就会向调用方法返回一个值。执行环境通过恢复调用方的寄存器并增加调用方的程序计数器以跳过方法调用指令来处理正常的方法返回。然后在调用方法的执行环境中继续执行程序。

如果当前方法的执行正常完成,则将值返回给调用方法。当调用方法执行适合于返回类型的返回指令时,会发生这种情况。

如果调用方法执行了不适合返回类型的返回指令,则该方法将引发异常或错误。可能发生的错误包括动态链接失败(例如,找不到类文件)或运行时错误(例如,在数组范围之外的引用)。发生错误时,执行环境将生成异常。

垃圾收集堆

在Java运行时环境中运行的每个程序都分配有一个垃圾回收堆。因为类对象的实例是从此堆分配的,所以堆的另一个词是内存分配池。默认情况下,在大多数系统上,堆大小设置为1MB。

尽管在启动程序时将堆设置为特定大小,但是例如在分配新对象时,堆可能会增长。为了确保堆不会变得太大,Java虚拟机会自动释放或垃圾回收不再使用的对象。

Java执行自动垃圾收集作为后台线程。在Java运行时环境中运行的每个线程都有两个与之关联的堆栈:第一个堆栈用于Java代码;第二个堆栈用于Java代码。第二个用于C代码。这些堆栈使用的内存是从总系统内存池中提取的。每当新线程开始执行时,就会为Java代码和C代码分配最大的堆栈大小。默认情况下,在大多数系统上,Java代码堆栈的最大大小为400KB,而C代码堆栈的最大大小为128KB。

如果我们的系统有内存限制,我们可以强制Java执行更积极的清理,从而减少使用的内存总量。为此,请减小Java和C代码堆栈的最大大小。如果我们的系统有很多内存,我们可以强制Java执行不太积极的清理,从而减少后台处理量。为此,请增加Java和C代码堆栈的最大大小。

恒量池

堆中的每个类都有一个与之关联的常量池。因为常量不变,所以它们通常在编译时创建。常量池中的项目编码特定类中任何方法使用的所有名称。该类包含一个对存在多少个常量的计数,以及一个偏移量,该偏移量指定在类说明中特定的常量列表从何处开始。

与常量关联的所有信息均基于常量的类型遵循特定格式。例如,类级常量用于表示类或接口,并具有以下格式:

CONSTANT_Class_info {
u1 tag;
u2 name_index;
}

tag的值在哪里CONSTANT_Class,并且name_index提供string了类的名称。类名int[][][[I。类名Thread[][Ljava.lang.Thread;

方法领域

Java的方法区域类似于其他编程语言使用的运行时环境的已编译代码区域。它存储与已编译代码中的方法相关联的字节码指令,以及执行环境进行动态链接所需的符号表。与该方法相关联的任何调试信息或其他信息也都存储在此区域中。

字节码指令集

尽管程序员更喜欢以高级格式编写代码,但是我们的计算机无法直接执行此代码,这就是为什么我们必须先编译Java程序,然后才能运行它们。通常,编译后的代码可以是称为机器语言的机器可读格式,也可以是诸如汇编语言或Java字节码的中间级格式。

Java虚拟机使用的字节码指令类似于汇编程序指令。如果您曾经使用过Assembler,则知道为了效率起见,将指令集精简到了最少,并且使用一系列指令来完成诸如打印到屏幕之类的任务。例如,Java语言允许我们使用单行代码在屏幕上打印,例如:

System.out.println(“Hello world!”);

在编译时,Java编译器将单行打印语句转换为以下字节代码:

0 getstatic #6 <Field java.lang.System.out Ljava/io/PrintStream;>
3 ldc #1 <String “Hello world!”>
5 invokevirtual #7 <Method java.io.PrintStream.println(Ljava/lang/String;)V>
8 return

JDK提供了一种用于检查字节码的工具,称为Java类文件反汇编程序。我们可以通过在命令行中键入javap来运行反汇编程序。

由于字节码指令的格式很低,因此我们的程序几乎以编译为机器语言的程序的速度执行。机器语言中的所有指令均由0和1的字节流表示。在低级语言中,0和1的字节流被适当的助记符(例如字节代码指令)代替isub。与汇编语言一样,字节码指令的基本格式为:

<operands(s)>

因此,字节码指令集中的一条指令由一个1字节的操作码(指定要执行的操作)以及零个或多个提供该操作将使用的参数或数据的操作数组成。

摘要

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数Java工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注Java获取)

img

最后

《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!
]

《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值