赞
踩
图2-JVM处理翻译
在我们的计算机内存中创建虚拟机需要构建真实计算机的每个主要功能,直到程序运行所在的环境。这些功能可以分为七个基本部分:
该寄存器 Java虚拟机的类似于在我们的电脑寄存器。但是,由于虚拟机是基于堆栈的,因此其寄存器不用于传递或接收参数。在Java中,寄存器保存机器的状态,并在执行每一行字节代码后进行更新以保持该状态。以下四个寄存器保存虚拟机的状态:
所有这些寄存器均为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面试题解析+后端开发学习笔记+最新架构讲解视频+实战项目源码讲义》
【docs.qq.com/doc/DSmxTbFJ1cmN1R2dB】 完整内容开源分享
存储在此区域中。
尽管程序员更喜欢以高级格式编写代码,但是我们的计算机无法直接执行此代码,这就是为什么我们必须先编译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来运行反汇编程序。
va/lang/String;)V>
8 return
JDK提供了一种用于检查字节码的工具,称为Java类文件反汇编程序。我们可以通过在命令行中键入javap来运行反汇编程序。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。