操作数栈

每一个方法调用,JVM会建立一个操作数栈,所有的操作码都是对操作数栈中的数据进行操作

aload_1

当我们调用一个方法,需要一个参数的时候,前提是需要知道这个参数的具体描述符,比如一个View,就是Landroid/view/View,无论是invokestatic还是invokevirtual其他指令,调用是会去操作数栈拿取需要的值或者类型,拿完之后就是pop出栈,因此需要先入栈

aload的格式为 aload ,N的具体数值表示为在_LocalVariableTable中的Index索引中

ILOAD, LLOAD, FLOAD, DLOAD ALOAD 指令读取一个局部变量,并将它的值压到操 作数栈中。它们的参数是必须读取的局部变量的索引 i
ILOAD 用于加载一个 booleanbytecharshort int 局部变量。
LLOADFLOAD DLOAD 分别用于加载 longfloat double值。(LLOAD DLOAD 实际加载两个槽 i i+1)。
最后,ALOAD 用于加载任意非基元值,即对 象和数组引用。
与之对应,ISTORELSTOREFSTOREDSTORE ASTORE 指令从操作数栈 中弹出一个值,并将它存储在由其索引 i 指定的局部变量中。

JVM指令之invokestatic,invokespecial,invokeinterface,invokevirtual,invokedynamic

指令 说明
invokeinterface 用以调用接口方法,在运行时搜索一个实现了这个接口方法的对象,找出适合的方法进行调用。(Invoke interface method)
invokevirtual 指令用于调用对象的实例方法,根据对象的实际类型进行分派(Invoke instance method; dispatch based on class)
invokestatic 用以调用类方法(Invoke a class (static) method )
invokespecial 指令用于调用一些需要特殊处理的实例方法,包括实例初始化方法、私有方法和父类方法。(Invoke instance method; special handling for superclass, private, and instance initialization method invocations )
invokedynamic JDK1.7新加入的一个虚拟机指令,相比于之前的四条指令,他们的分派逻辑都是固化在JVM内部,而invokedynamic则用于处理新的方法分派:它允许应用级别的代码来确定执行哪一个方法调用,只有在调用要执行的时候,才会进行这种判断,从而达到动态语言的支持。(Invoke dynamic method)

$1

字节码中会出现这样的字符串

image.png

$1代表着第一个匿名内部类

内联

概念:把函数调用的方法直接内嵌到方法内部,减少函数调用的次数

函数的调用过程: 调用某个函数实际上将程序执行顺序转移到该函数所存放在内存中某个地址,将函数的程序内容执行完后,
再返回到 转去执行该函数前的地方。这种转移操作要求在转去前要保护现场并记忆执行的地址,转回后先要
恢复 现场,并按原来保存地址继续执行。也就是我们常说的压栈和出栈。

当我们在调用频率很高的地方经常调用一个其他函数,但是函数体很小,函数频繁的函数跳转影响性能。这时候jvm
能够自动识别热点函数,到达阀值触发jit优化。对其进行内联。

以getter/setter为例,如果没有方法内联,在调用getter/settter时,程序需要保存当前方法的执行位置,创建并压入用于getter/setter的栈桢、访问字段、弹出栈桢、最后恢复当前方法的执行。而当内联了对getter/setter的方法调用后,上述操作仅剩字段访问。