热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

java设置栈进行路径探索_探究Java虚拟机栈—Java进阶

前言Java虚拟机的内存模型分为两部分:一部分是线程共享的,包括Java堆和方法区;另一部分是线程私有的,包括虚拟机栈和本地

前言

Java 虚拟机的内存模型分为两部分:一部分是线程共享的,包括 Java 堆和方法区;另一部分是线程私有的,包括虚拟机栈和本地方法栈,以及程序计数器这一小部分内存。今天我就 Java 虚拟机栈做一些比较浅的探究。

熟悉 Java 的同学应该都知道了,JVM 是基于栈的。但是这个“栈” 具体指的是什么?难道就是虚拟机栈?想要回答这个问题我们先要从虚拟机栈的结构谈起。

虚拟机栈

何为虚拟机栈

虚拟机栈的栈元素是栈帧,当有一个方法被调用时,代表这个方法的栈帧入栈;当这个方法返回时,其栈帧出栈。因此,虚拟机栈中栈帧的入栈顺序就是方法调用顺序。什么是栈帧呢?栈帧可以理解为一个方法的运行空间。它主要由两部分构成,一部分是局部变量表,方法中定义的局部变量以及方法的参数就存放在这张表中;另一部分是操作数栈,用来存放操作数。我们知道,Java 程序编译之后就变成了一条条字节码指令,其形式类似汇编,但和汇编有不同之处:汇编指令的操作数存放在数据段和寄存器中,可通过存储器或寄存器寻址找到需要的操作数;而 Java 字节码指令的操作数存放在操作数栈中,当执行某条带 n 个操作数的指令时,就从栈顶取 n 个操作数,然后把指令的计算结果(如果有的话)入栈。因此,当我们说 JVM 执行引擎是基于栈的时候,其中的“栈”指的就是操作数栈。举个简单的例子对比下汇编指令和 Java 字节码指令的执行过程,比如计算 1 + 2,在汇编指令是这样的:mov ax,1;把1放入寄存器 axadd ax,2;用 ax 的内容和2相加后存入 ax

而 JVM 的字节码指令是这样的:iconst_1 //把整数 1 压入操作数栈iconst_2 //把整数 2 压入操作数栈iadd //栈顶的两个数相加后出栈,结果入栈

由于操作数栈是内存空间,所以字节码指令不必担心不同机器上寄存器以及机器指令的差别,从而做到了平台无关。

注意,局部变量表中的变量不可直接使用,如需使用必须通过相关指令将其加载至操作数栈中作为操作数使用。比如有一个方法 void foo(),其中的代码为:int a = 1 + 2; int b = a + 3;,编译为字节码指令就是这样的:iconst_1 //把整数 1 压入操作数栈iconst_2 //把整数 2 压入操作数栈iadd //栈顶的两个数出栈后相加,结果入栈;实际上前三步会被编译器优化为:iconst_3istore_1 //把栈顶的内容放入局部变量表中索引为 1 的 slot 中,也就是 a 对应的空间中iload_1 // 把局部变量表索引为 1 的 slot 中存放的变量值(3)加载至操作数栈iconst_3 iadd //栈顶的两个数出栈后相加,结果入栈istore_2 // 把栈顶的内容放入局部变量表中索引为 2 的 slot 中,也就是 b 对应的空间中return// 方法返回指令,回到调用点

需要说明的是,局部变量表以及操作数栈的容量的较大值在编译时就已经确定了,运行时不会改变。并且局部变量表的空间是可以复用的,例如,当指令的位置超出了局部变量表中某个变量 a 的作用域时,如果有新的局部变量 b 要被定义,b 就会覆盖 a 在局部变量表的空间。

盗用别人的图以让大家对虚拟机栈有个直观的认识(其中小字体 Stack 指的的是虚拟机栈,Frame 是栈帧,Local variables 是局部变量表,Operand Stack 是操作数栈):

c22fcc1a20699c9a8b97ce2818e31106.png

由虚拟机栈引出的问题

看完上面的代码大家可能会有几点疑惑:什么是 slot?那些指令是什么意思?为什么 a 对应的 slot 的索引值不是从零开始的,它明明是靠前个定义的变量啊?

对于这些问题我们一个个来解决。

什么是 slot

首先什么是 slot?slot 是局部变量表中的空间单位,虚拟机规范中有规定,对于 32 位之内的数据,用一个 slot 来存放,如 int,short,float 等;对于 64 位的数据用连续的两个 slot 来存放,如 long,double 等。引用类型的变量 JVM 并没有规定其长度,它可能是 32 位,也有可能是 64 位的,所以既有可能占一个 slot,也有可能占两个 slot。

JVM 字节码指令

第二个问题,那些指令是什么意思?

指令格式

首先我们要理解 Java 指令的格式,Java 的指令以字节为单位,也就是一个字节代表一条指令。比如 iconst_1 就是一条指令,它占一个字节,那么自然 Java 指令不会超过 256 条。实际上 Java 指令目前定义了 200 多条。指令虽然是一个字节,但是它也可以带自己的操作数。JVM 中有这样一条指令 putstatic,其作用是给特定的的静态字段赋值。但是给哪个字段赋值呢?仅仅通过这条指令并不能说明,那么只有通过操作数来指定了。紧跟在 putstatic 后面的两个字节就是它的操作数,这个操作数是一个索引值,指向运行时常量池中该静态字段对应的符号引用。由于符号引用包含了该字段的基本信息,如所属类、简单名称以及描述符,因此 putstatic 指令就知道是给哪个类的哪个字段赋值了。

指令的操作数分两种:一种是嵌入在指令中的,通常是指令字节后面的若干个字节;另一种是存放在操作数栈中的。为了区别,我们把前者叫做嵌入式操作数,把后者叫做栈内操作数。这两者的区别是:嵌入式操作数是在编译时就已经确定的,运行时不会改变,它和指令一样存放于类文件方法表的 Code 属性中;而操作数是运行时确定的,即程序在执行过程中动态生成的。拿 putstatic 指令来说,它有一个嵌入式操作数,该操作数是一个索引值(前面已经提到),它由两个字节组成,紧跟在 putstatic 对应的字节之后;同时它还有一个栈内操作数,位于操作数栈的栈顶,这个操作数就是要赋给静态字段的值,其对应的字节数根据静态字段的类型决定。如果静态字段的类型是 short、int、boolean、char 或者 byte,那么这个操作数就必须是 int 类型,即由栈顶的 4 个字节组成;如果是 float、double 或者 long 类型,那么操作数就是相应的类型,即由栈顶的 4 个、8 个 或者 8 个 字节组成;如果静态字段是引用类型,那么这个操作数的类型也必须是引用类型,即由栈顶的 8 个字节组成。

再举一个例子。iconst_ 代表了一个指令族,它的意思是把整数 i 放入操作数栈中,i 的范围是(m1, 0, 1, 2, 3, 4, 5),其中 m1 代表的是 -1。注意,这里的 i 并不是指令的操作数(即非嵌入式操作数,也非栈内操作数),如 iconst_1、iconst_2 和 iconst3 都是由一个字节组成的字节码指令。我们可以把 i 可以看作是指令的 “隐含操作数”,即指令本身就蕴含了操作数。如果整数 i 超过 [-1, 5] 这个范围,就不能用 iconst 表示了,因为仅一个字节的字节码指令不可能蕴含所有的整数。此时就需要 bipush 这条指令了,这条指令有一个嵌入式操作数,由一个字节组成,用来表示要放入栈顶的那个整数,该整数放入栈顶时通过扩展符号位变为 32 位的整型。但是一个字节也表示不了所有的整数,如果整数值超过一个字节所能表示的范围,就只能通过 ldc 这条指令了,这条指令带有一个字节的嵌入式操作数,它代表的是一个指向运行时常量池中 Constant_Integer_info 类型常量的索引,通过索引的方式引用运行时常量池中的整数,再大的整数也不怕了。



推荐阅读
  • 本文介绍了Java工具类库Hutool,该工具包封装了对文件、流、加密解密、转码、正则、线程、XML等JDK方法的封装,并提供了各种Util工具类。同时,还介绍了Hutool的组件,包括动态代理、布隆过滤、缓存、定时任务等功能。该工具包可以简化Java代码,提高开发效率。 ... [详细]
  • 如何搭建Java开发环境并开发WinCE项目
    本文介绍了如何搭建Java开发环境并开发WinCE项目,包括搭建开发环境的步骤和获取SDK的几种方式。同时还解答了一些关于WinCE开发的常见问题。通过阅读本文,您将了解如何使用Java进行嵌入式开发,并能够顺利开发WinCE应用程序。 ... [详细]
  • JavaScript简介及语言特点
    本文介绍了JavaScript的起源和发展历程,以及其在前端验证和服务器端开发中的应用。同时,还介绍了ECMAScript标准、DOM对象和BOM对象的作用及特点。最后,对JavaScript作为解释型语言和编译型语言的区别进行了说明。 ... [详细]
  • 初识java关于JDK、JRE、JVM 了解一下 ... [详细]
  • VScode格式化文档换行或不换行的设置方法
    本文介绍了在VScode中设置格式化文档换行或不换行的方法,包括使用插件和修改settings.json文件的内容。详细步骤为:找到settings.json文件,将其中的代码替换为指定的代码。 ... [详细]
  • Nginx使用(server参数配置)
    本文介绍了Nginx的使用,重点讲解了server参数配置,包括端口号、主机名、根目录等内容。同时,还介绍了Nginx的反向代理功能。 ... [详细]
  • 原文地址:https:www.cnblogs.combaoyipSpringBoot_YML.html1.在springboot中,有两种配置文件,一种 ... [详细]
  • C语言注释工具及快捷键,删除C语言注释工具的实现思路
    本文介绍了C语言中注释的两种方式以及注释的作用,提供了删除C语言注释的工具实现思路,并分享了C语言中注释的快捷键操作方法。 ... [详细]
  • Voicewo在线语音识别转换jQuery插件的特点和示例
    本文介绍了一款名为Voicewo的在线语音识别转换jQuery插件,该插件具有快速、架构、风格、扩展和兼容等特点,适合在互联网应用中使用。同时还提供了一个快速示例供开发人员参考。 ... [详细]
  • Java在运行已编译完成的类时,是通过java虚拟机来装载和执行的,java虚拟机通过操作系统命令JAVA_HOMEbinjava–option来启 ... [详细]
  • FineReport平台数据分析图表显示部分系列接口的应用场景和实现思路
    本文介绍了FineReport平台数据分析图表显示部分系列接口的应用场景和实现思路。当图表系列较多时,用户希望可以自己设置哪些系列显示,哪些系列不显示。通过调用FR.Chart.WebUtils.getChart("chartID").getChartWithIndex(chartIndex).setSeriesVisible()接口,可以获取需要显示的系列图表对象,并在表单中显示这些系列。本文以决策报表为例,详细介绍了实现方法,并给出了示例。 ... [详细]
  • 开发笔记:Python之路第一篇:初识Python
    篇首语:本文由编程笔记#小编为大家整理,主要介绍了Python之路第一篇:初识Python相关的知识,希望对你有一定的参考价值。Python简介& ... [详细]
  • Android系统启动过程分析一、Android平台架构首先贴一张Android系统架构图方便理解整个Android架构,这可以让我们从整体上对整个启动流程有个大概认知。可以看出整 ... [详细]
  • Nginx使用AWStats日志分析的步骤及注意事项
    本文介绍了在Centos7操作系统上使用Nginx和AWStats进行日志分析的步骤和注意事项。通过AWStats可以统计网站的访问量、IP地址、操作系统、浏览器等信息,并提供精确到每月、每日、每小时的数据。在部署AWStats之前需要确认服务器上已经安装了Perl环境,并进行DNS解析。 ... [详细]
  • 本文介绍了RPC框架Thrift的安装环境变量配置与第一个实例,讲解了RPC的概念以及如何解决跨语言、c++客户端、web服务端、远程调用等需求。Thrift开发方便上手快,性能和稳定性也不错,适合初学者学习和使用。 ... [详细]
author-avatar
AMY_Only
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有