济宁旅游景点大全:Java ASM学习(2)

admin 5个月前 (04-29) 科技 36 0

1.编译后的方式区,其中存储的代码都是一些字节码指令

2.Java虚拟机执行模子:

java代码是在一个线程内部执行,每个线程都有自己的执行栈,栈由帧组成,每个帧示意一个方式的挪用,每挪用一个方式,都将将新的帧压入执行栈,方式返回时(不管是整成return照样异常返回),该方式对应的帧都将出栈,即根据先进后出的规则。

执行栈与操作数栈不一样,操作数栈包含在执行栈中。每一帧包罗局部变量和操作数栈两部门,操作数栈中包罗字节码指令用来当操作数的值。好比a.equals(b)将建立一帧,此时该帧将有一个空栈,而且a和b作为局部变量

字节码指令:

由标识该指令的操作码和牢固数目的参数组成,操作码指定要举行哪一类操作,参数指定详细正确行为。指令分为两类,一类在局部变量和操作数栈之间传值,一类从操作数栈弹出值盘算后再压入

例如:

ILOAD,LLOAD,FLOAD,DLOAD,ALOAD读取一个局部变量,并将其值压入操作数栈中,其对应的参数是其读取的局部变量索引i(由于局部变量就是通过索引来举行随机接见的),LLOAD和DLOAD加载时需要两个槽(slot),由于局部变量部门和操作数占部门的每个槽(slot)都可以保留除了long和double之外的java值(long和double需要两个槽)。

ILOAD:加载boolean、charbyteshort、int局部变量
LLOAD:加载long
FLOAD:加载float
DLOAD:加载double
ALOAD:加载工具和数组引用

对应的ISTORE,LSTORE,FSTORE,DSTORE,ASTORE从操作数栈弹出值并将其存储在指定的索引i所代表的局部变量中,以是这些操作指令是和java数据类型密切相关的。存取值和数据类型也相关,好比使用ISTORE 1 ALOAD 1,此时从操作数栈弹出一个int值存入索引1处的局部变量中,再将该值转为工具类型举行转换读取是非法的。然则对于一个局部变量位置,我们可以在运行过程中改变其类型,好比ISTORE 1 ALOAD 1非法,然则ATORE 1 ALOAD1就正当了。详细的字节码指令见ASM指南附A.1

通过一个例子来举行学习,好比以下方式:

package asm;

public class bean {
    private int f;

    public bean() {
    }

    public void setF(int f) {
        this.f = f;
    }

    public int getF() {
        return this.f;
    }
}

直接通过字节码文件查看其class文件结构,其字段就一个int类型的f,接见修饰符为private

setf方式的字节码指令如下

 其局部变量表如下,以是有两个值一个就是当前工具this和成员变量f,划分对应下标0和1

 这里要设计到几个字节码指令:

GETFIELD owner name desc:读取一个字段的值并将其值压入操作数栈中
PUTFIELD owner name desc:从操作数弹出值存在name所代表的字段中
owner:类的全限命名
GETSTATIC owner name desc和PUTSTATIC owner name desc类似,只是为静态变量

aload 0,读取局部变量this,也就是局部变量表下标为0处的this工具(其在挪用这个方式的时刻就已经初始化存储在局部变量表中),然后将其压入操作数栈。

iload 1,读取局部变量f,下标为1(建立帧时代已经初始化,也就是入口参数int f),压入操作数栈中

putfield #2 <asm/bean.f> 也就是弹出压入的两个值,赋值给asm/bean.f,也就是将入口的int f的值赋给this.f

return 即该方式执行完成,那么该帧从执行栈从弹出

getf对应的字节码指令如下所示:

济宁旅游景点大全:Java ASM学习(2) 第1张

aload 0,即从局部变量表拿到this放入操作数栈

getfield #2 <asm/bean.f> 即从操作数栈中拿出this,并将this.f的值压入操作数栈

ireturn 返回f的值get方式的挪用者,xreturn,x即返回变量对应的修饰符

bean组织方式,字节码指令如下:

济宁旅游景点大全:Java ASM学习(2) 第2张

aload 0: 从局部变量表拿到this,压入操作数栈

这里要设计方式的挪用相关的字节码指令:

INVOKEVIRTUAL owner name desc:
挪用owner所示意的类的name方式
desc用来形貌一个方式的参数类型和返回类型 INVOKESTATIC:挪用静态方式 INVOKESPECIAL: 挪用私有方式和组织器 INVOKEINTERFACE: 接口中界说的方式

invokespecial #1 <java/lang/Object.<init>>: 挪用object工具的init方式,即super()挪用,最后return返回,若是是对于以下代码:

package asm;

public class bean {
    private int f;

    public void setFf(int f) {
        if(f>0){
        this.f = f;}
        else {
            throw new IllegalArgumentException();
        }
    }

    public int getF() {
        return f;
    }

}

此时setf的字节码指令如下:

济宁旅游景点大全:Java ASM学习(2) 第3张

iload  1,从局部表量表中拿出入口参数 int f,压入操作数栈

ifile 9:此时弹出操作数栈中的int f和0举行对照

a.若是小于即是0(这里将大于判断转为小于即是的判断),则到第12条指令 

new #2 :新建一个异常工具并压入操作数栈

dup:重复压入该值一次

invokespecial #4  : 弹出操作栈中两个工具值其中之一,并挪用其组织函数实例化该工具

athrow:弹出操作数栈中剩下的值(另一个异常工具),并将其作为异常抛出

b.若是大于0,则依次执行

aload0 从局部变量表拿出this工具放入操作数栈中

iload1 拿出入口int f的值压入栈中

putfiled #2 <asm/bean.f>:将int f的值赋给this.f

goto 20: 到第20条字节码指令

return : 返回

感受和汇编有点像,不外比汇编更容易明白,主要照样方式内的一些操作,能看懂基本的字节码指令,庞大的再去查doc,听说面试有时刻会问i++和++i的区别:

package asm;

public class testplus {

    public void plusf(){
        int i=0;
        System.out.println(i++);
    }
       public void pluse(){
        int i=0 ;
        System.out.println(++i);
       }
}

编译后:

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

package asm;

public class testplus {
    public testplus() {
    }
  //i++
    public void plusf() {
        int i = 0;
        byte var10001 = i;
        int var2 = i + 1;
        System.out.println(var10001);
    }
  //++i
    public void pluse() {
        int i = 0;
        int i = i + 1;
        System.out.println(i);
    }
}

首先从天生的class来看,i++编译后竟然用字节存储了i的值,然后i自增1,输出的为字节类型i即0,以是i++,最终输出为0,++i,直接是i自增1,然后输出i,以是最终输出为1以是for循环用i++,而不用++i

从字节码指令来看:

i++

济宁旅游景点大全:Java ASM学习(2) 第4张

iconst 0:首先操作数栈中压入常量0

istore 1:然后弹出常量0放入局部变量表索引1处,此时局部变量表处1处从i变为0,操作数栈空

getstatic #2 :即拿到java.lang.System.out,即取静态变量System.out压入栈中,此时栈中1元素

 

济宁旅游景点大全:Java ASM学习(2) 第5张

#2在常量池中为第二个,关于该字段的引用说明如下,out对应的形貌符即为Ljava/io/PrintStream; 那么类类型的形貌符就是L+类的全限命名+;

 济宁旅游景点大全:Java ASM学习(2) 第6张

iload 1:从局部变量表1处取值,压住操作数栈,即将0压入操作数栈

iinc 1 by 1:给局部变量1处的值+1,此时1处即从0变为1

invokevirtual:挪用java.io.PrintStream.println,此时需要的值是从操作数栈中取的,然而此时操作数栈顶弹出的数值为0,以是输出为0

++i

 济宁旅游景点大全:Java ASM学习(2) 第7张

iconst 0:首先操作数栈中压入常量0

istore 1:然后弹出常量0放入局部变量表索引1处,此时局部变量表处1处从i变为0,操作数栈空

getstatic #2 :即拿到java.lang.System.out,即取静态变量System.out压入栈中,此时栈中1元素

iinc 1 by 1:将局部变量表1处的值加1,即从0变为1

iload 1:加载局部变量表1处的值,压入操作数栈中,即将1压入栈中

invokevirtual:挪用java.io.PrintStream.println,此时需要的值是从操作数栈中取的,然而此时操作数栈顶弹出的数值为1,以是输出为1

以是i++和++i的区别从字节码指令上来看就是局部变量表自增和压入操作数栈的顺序不一样,i++是先压栈,后局部变量表自增,++i是先局部变量表自增,后压入操作数栈,这样就完全搞懂了2333~

以是再剖析一个牢固牢固:

package asm;

public class testplus {

       public void pluse(){
        int i=0 ;
        int p = 2 + i++ - ++i;
        System.out.println(i);
        System.out.println(p);
       }

    public static void main(String[] args) {
        testplus t = new testplus();
        t.pluse();
    }
}

main方式:

济宁旅游景点大全:Java ASM学习(2) 第8张

new #4 <asm/testplus>:new一个工具压入栈中

dup:赋值一个栈顶的工具再压入操作数栈,关于为什么要压入两个重复的值缘故原由:

首先字节码指令操作数值时基于栈实现的,那么对于同一个值从栈中操作时必定要弹出,那么若是对一个数同时操作两次,那么就要两次压栈。涉及到new一个工具操作时,java虚拟机自动dup,在new一个工具以后,栈中放入的是该工具在堆中的地址,好比声明以下两个

class1 a = new class1();
a.pp()

通常在挪用工具挪用其类中方式前肯定要挪用其init实例化,那么init要用一次操作数栈中的地址,此时弹出一次地址介入方式挪用,后面只需要再将该栈中的地址放入局部变量表,该地址的工具已经完成了实例化操作,那么后面每次挪用只需要从局部变量表从取到该工具的地址,即可随便挪用其类中的方式。

invokespecial #5 :这里挪用testplus的init方式,以是从栈中弹出一个testplus的地址

astore 1:将实例化以后的该testplus工具地址放入局部变量表1处

aload 1:取局部变量表1处的工具地址压入栈中

invokevirtual #6:挪用testplus的pluse方式

return :返回

pluse方式:

济宁旅游景点大全:Java ASM学习(2) 第9张

 

iconst 0:压入常量0

istore 1:弹出0存入局部变量表1处 (完成int i=0)

iconst 2:将2压入栈中

iload 1:取出局部变量表1处的值0压入栈中

iinc 1 by 1:局部变量表1处的值加1,即从0变为1

iadd :将栈中的两个值相加,即 stack[0] + stack[1] = 2 + 0 =2

iinc 1 by 1: 局部变量表1处的值加1,即从1变为2

iload 1:去局部变量表1处的值压入栈中,即栈顶为2

isub :将栈中两个元素相减,即stack[0] - stack[1] =  2 - 2 =0

istore 2:弹出栈中的唯一一个元素2,存入局部变量表2处,此时栈空

getstatic # 2 :拿到Syetem.out,压入栈中

iload 1:取出局部表量表1处的值压入栈中,即栈顶为2

invokevirtual  #3 : 弹出栈中两个元素,挪用System.out的println方式,即stack[0].print(stack[1]),即输出2

同理压入System.out,然后iload 2,取出局部变量表2处的0压入栈中,输出0

最终输出效果也是2和0

济宁旅游景点大全:Java ASM学习(2) 第10张

,

Sunbet

Sunbet www.862169.com www.sunbet.us是进入Sunbet的官方站点。Sunbet开放Sunbet会员开户网址、Sunbet代理开户、Sunbet手机版下载、Sunbet电脑客户端下载等业务。

皇冠体育声明:该文看法仅代表作者自己,与本平台无关。转载请注明:济宁旅游景点大全:Java ASM学习(2)

网友评论

  • (*)

最新评论

文章归档

站点信息

  • 文章总数:532
  • 页面总数:0
  • 分类总数:8
  • 标签总数:936
  • 评论总数:160
  • 浏览总数:2876