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

【ASM系列2】字节码介绍

这一篇首先介绍下面这些类型的字节码指令:装载和存储系列:从局部变量装载到操作数栈的xload系列指令、从常量池中装载数据到操作数栈的xcon

           这一篇首先介绍下面这些类型的字节码指令:

           装载和存储系列:从局部变量装载到操作数栈的xload系列指令、从常量池中装载数据到操作数栈的xconst和push系列、将操作数栈中的数据保存到局部变量的xstore系列指令。

           字段访问系列:getfield、putfield、getstatic、putstatic等指令。

           方法调用系列:invokevirtual、invokeinterface、invokespecial、invokestatic等指令。

           方法返回系列:ireturn、lreturn、freturn、dreturn、areturn等指令。

           从一个简单的例子开始看,写一个类:

           

           然后来到.class文件的目录下,执行javap -verbose -c Main,verbose参数的作用是指定javap显示常量池,而-c的作用是指定javap显示方法的字节码。执行结果如下:

                    

           其中第一部分是常量池,从来保存例如字符串、类名、方法名等常量项,这些常量项又有着固定的格式。首先,每个常量项都有一个标识,例如字符串是Asciz,类是class,方法是Method。而常量项又可以包含其他常量项,比如Method常量项就包含一个class常量项和方法的NameAndType常量项,而class常量又包含类名的字符串常量项,方法的NameAndType常量项又包含方法名的字符串常量项和方法签名的字符串常量项。

           下面是每个方法的描述,第一个是Main方法的构造函数,这里我们也可以指定,类中没有显示写构造函数的时候,jvm会给我们在编译的时候自动生成一个构造函数。它的内容只要三个操作:aload_0、invokespecial #8和return。

           load系列的的字节码作用是把本地变量加载到操作数栈当中。比如:iload、lload、fload、dload等是针对不同的类型的操作码,i表示int,l表示long,f表示fload,d表示double,a表示引用,在java的字节码当中,大部分的操作数据的指令都是这个规律。

          延伸一下,和load系列指令对应的是xstore系列的指令,它的作用是把操作数栈中的数据存储到本地变量中,对应的也有istore,lstore,fstore,dstore,astore。

          aload_0:aload的意思是从本地变量列表中加载一个引用到操作数栈中,后面的操作数(这里是0)指示了要加载的本地变量的偏移量。在这里指的是加载方法的this本地变量,在实例对象的方法中,第一个本地变量永远是this。此外,在构造函数中,往往第一个字节码都是aload_0,因为紧接着的默认调用是父类的构造器,super方法需要当前实例作为入参。

           invoke系列的字节码包括:invokevirtual、invokeinterface、invokespecial以及invokestatic。invokevirtual是调用对象的成员方法,invokeinterface是调用接口上的方法,invokespecial是指调用类内部的private方法,而invokestatic则是指调用指定类的静态方法。

           在这里,Main类的构造函数中,invokevirtual #8指定了要调用的是常量池第8位表示的方法,即java.lang.Object的初始化方法,并且使用了aload_0这个步骤放到操作数栈顶的this对应引用作为Object构造函数的第一个也是唯一的参数。

          最后看return系列的指令,这系列的指令也是类似得有ireturn、lreturn、freturn、dreturn、areturn,分别和之前讲到load系列的指令里的i、l、f、d、a对应,标识了方法返回不同的类型,而return系列还多了一种类型的指令是return,它表示void类型方法的返回。相比其他有返回值的return,return指令在返回的时候不会去栈中load数据,而其余几个都会在返回之前从操作数栈中获取返回值,其中lreturn和dreturn会稍微特殊,他们需要从操作数栈中弹出两位,其余的几个都只要弹出一位就直接返回了。

           接下来来看main方法,第一个操作,getstatic #16,常量池第16项的常量项是一个静态的Field常量项,标识java.lang.System的out静态字段。getstatic的意义是把静态字段中的值加载到当前帧的操作栈中。紧接着的操作是ldc #22,它的意义是把常量池22位的字符串常量加载到操作数栈顶,这样,栈顶两项就分别是java.io.PrintStream的对象和String常量“hello world”了。紧接着,invokevirtual指令将要来消费栈顶这两项的操作数了,它的操作数是#24,即常量池第24项的println方法的常量项,它指定了要调用的对象方法。这个指令会消费栈底的java.io.PrintStream的对象作为要调用的对象,并消费栈顶的字符串常量作为println方法的入参。这样,就完成了main方法中指定的所有逻辑了。

           getstatic属于访问字段的指令,这一系列指令包括:getfield、putfield、getstatic、putstatic,getfield操作从一个对象中获取指定的字段值,并把得到的值放入操作数栈顶。getfield指令要执行,指令后需要带一个指向常量池中字段常量项的偏移量,并且操作数栈顶已经压入了需要从中取数据的目标对象的引用了。而putfield指令则刚好相反,它根据操作数栈顶的值和操作数栈顶之下一位的目标对象的引用,并在指令之后带一个指向常量池中字段常量项的偏移量,给目标操作的对象的指定字段设置值。而getstatic和putstatic两个指令和前面两个指令作用基本相同,只不过他们操作的是类的静态字段,所以他们在执行这两个指令的时候,操作数栈中并不需要对象的引用,即:对于getstatic,执行指令时不需要从操作数栈中获取数据;对于putstatic,执行指令时,只需要栈顶的一项,即即将要给静态字段赋的值。

           ldc指令的作用是把常量池中对应的内容压入操作数栈中,指令之后紧跟着的操作数是一个指向常量池偏移量的数字。和ldc指令类似,同样能装载数据到操作数栈上的指令有:bipush、sipush、ldc2、aconst_null、iconst_、fconst_、lconst_等。其中bipush是把byte类型的值压入栈顶,sipush是把short类型的值压入栈顶,aconst_null是把null压入栈顶,iconst_1是把int型值1压入栈顶,fconst_0是压入0f,lconst_1是压入long型值1,ldc2 是把常量池中表示double或者long型的数据压入栈,因为它们占了两位的栈空间。


推荐阅读
  • Java学习笔记之面向对象编程(OOP)
    本文介绍了Java学习笔记中的面向对象编程(OOP)内容,包括OOP的三大特性(封装、继承、多态)和五大原则(单一职责原则、开放封闭原则、里式替换原则、依赖倒置原则)。通过学习OOP,可以提高代码复用性、拓展性和安全性。 ... [详细]
  • 本文讨论了使用差分约束系统求解House Man跳跃问题的思路与方法。给定一组不同高度,要求从最低点跳跃到最高点,每次跳跃的距离不超过D,并且不能改变给定的顺序。通过建立差分约束系统,将问题转化为图的建立和查询距离的问题。文章详细介绍了建立约束条件的方法,并使用SPFA算法判环并输出结果。同时还讨论了建边方向和跳跃顺序的关系。 ... [详细]
  • 本文介绍了Oracle数据库中tnsnames.ora文件的作用和配置方法。tnsnames.ora文件在数据库启动过程中会被读取,用于解析LOCAL_LISTENER,并且与侦听无关。文章还提供了配置LOCAL_LISTENER和1522端口的示例,并展示了listener.ora文件的内容。 ... [详细]
  • CF:3D City Model(小思维)问题解析和代码实现
    本文通过解析CF:3D City Model问题,介绍了问题的背景和要求,并给出了相应的代码实现。该问题涉及到在一个矩形的网格上建造城市的情景,每个网格单元可以作为建筑的基础,建筑由多个立方体叠加而成。文章详细讲解了问题的解决思路,并给出了相应的代码实现供读者参考。 ... [详细]
  • 本文讨论了clone的fork与pthread_create创建线程的不同之处。进程是一个指令执行流及其执行环境,其执行环境是一个系统资源的集合。在调用系统调用fork创建一个进程时,子进程只是完全复制父进程的资源,这样得到的子进程独立于父进程,具有良好的并发性。但是二者之间的通讯需要通过专门的通讯机制,另外通过fork创建子进程系统开销很大。因此,在某些情况下,使用clone或pthread_create创建线程可能更加高效。 ... [详细]
  • 本文介绍了最长上升子序列问题的一个变种解法,通过记录拐点的位置,将问题拆分为左右两个LIS问题。详细讲解了算法的实现过程,并给出了相应的代码。 ... [详细]
  • 本文讨论了如何优化解决hdu 1003 java题目的动态规划方法,通过分析加法规则和最大和的性质,提出了一种优化的思路。具体方法是,当从1加到n为负时,即sum(1,n)sum(n,s),可以继续加法计算。同时,还考虑了两种特殊情况:都是负数的情况和有0的情况。最后,通过使用Scanner类来获取输入数据。 ... [详细]
  • 本文介绍了九度OnlineJudge中的1002题目“Grading”的解决方法。该题目要求设计一个公平的评分过程,将每个考题分配给3个独立的专家,如果他们的评分不一致,则需要请一位裁判做出最终决定。文章详细描述了评分规则,并给出了解决该问题的程序。 ... [详细]
  • 本文介绍了一种划分和计数油田地块的方法。根据给定的条件,通过遍历和DFS算法,将符合条件的地块标记为不符合条件的地块,并进行计数。同时,还介绍了如何判断点是否在给定范围内的方法。 ... [详细]
  • 本文讨论了一个关于cuowu类的问题,作者在使用cuowu类时遇到了错误提示和使用AdjustmentListener的问题。文章提供了16个解决方案,并给出了两个可能导致错误的原因。 ... [详细]
  • 本文介绍了P1651题目的描述和要求,以及计算能搭建的塔的最大高度的方法。通过动态规划和状压技术,将问题转化为求解差值的问题,并定义了相应的状态。最终得出了计算最大高度的解法。 ... [详细]
  • 本文介绍了UVALive6575题目Odd and Even Zeroes的解法,使用了数位dp和找规律的方法。阶乘的定义和性质被介绍,并给出了一些例子。其中,部分阶乘的尾零个数为奇数,部分为偶数。 ... [详细]
  • 如何搭建Java开发环境并开发WinCE项目
    本文介绍了如何搭建Java开发环境并开发WinCE项目,包括搭建开发环境的步骤和获取SDK的几种方式。同时还解答了一些关于WinCE开发的常见问题。通过阅读本文,您将了解如何使用Java进行嵌入式开发,并能够顺利开发WinCE应用程序。 ... [详细]
  • 本文讨论了在手机移动端如何使用HTML5和JavaScript实现视频上传并压缩视频质量,或者降低手机摄像头拍摄质量的问题。作者指出HTML5和JavaScript无法直接压缩视频,只能通过将视频传送到服务器端由后端进行压缩。对于控制相机拍摄质量,只有使用JAVA编写Android客户端才能实现压缩。此外,作者还解释了在交作业时使用zip格式压缩包导致CSS文件和图片音乐丢失的原因,并提供了解决方法。最后,作者还介绍了一个用于处理图片的类,可以实现图片剪裁处理和生成缩略图的功能。 ... [详细]
  • 本文介绍了如何使用Express App提供静态文件,同时提到了一些不需要使用的文件,如package.json和/.ssh/known_hosts,并解释了为什么app.get('*')无法捕获所有请求以及为什么app.use(express.static(__dirname))可能会提供不需要的文件。 ... [详细]
author-avatar
谢俊荣1792
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有