[color=brown] 【题外话】只要学过编译原理,计算机组成类似的课程的,就不会觉得JVM的字节码太难理解。有兴趣的人,只要边看书,边写几个例子后,再javap一下慢慢看几个就大概心里有数了,其实学习JVM的东西,主要是为了让自己对它的运行方式多一些了解,在实际编程过程中心里有个概念,有些特性的底层实现心里有数就行。
毕竟在大多数编程中,JVM的知识不会直接的影响到你的编程思路,但会潜移默化的让你绕过一些潜在的风险或者障碍。[/color]
下面稍微介绍两个概念:[b]接收参数[/b] 和 [b]方法调用[/b]
[b][size=large]接收参数[/size][/b]
实例方法(非静态方法)和类方法(静态方法)的实现是略有不同的,因为当调用一个实例方法时,首先会将该类的自身的实例的引用作为第0个参数放置在局部变量表中,然后才在局部变量表的第1到N的位置上放置参数(如果有参数, N是参数个数)。
而静态方法不需要传递自身实例的应用,所以参数是从第0个开始存放。
(要注意long和double所占局部变量的大小为2)
大家可以用下面这两个例子做下测试
//example_1
//example_2, static method
[b][size=large]方法调用[/size][/b]
对普通实例方法(即非静态方法)调用是在运行时根据对象类型进行分派的(相当于在C++中所说的“虚方法”), 这类方法通过调用invokevirtual指令实现。
下面看一个简单的例子,然后是其字节码:
其具体流程如下(图只是用来参考,并不一定完全正确...)
[b]STEP1:[/b]
从局部变量表load当前类实例的指针,将其push到操作数栈
[img]http://glutinit.iteye.com/upload/picture/pic/102548/0fc7515c-86a6-390b-b92b-ed04fe591f44.jpg[/img]
[b]
STEP2:[/b]
将常量12和13分别push到操作数栈
[img]http://glutinit.iteye.com/upload/picture/pic/102558/a4938cdf-e39c-3a6e-a2f9-fd203ed08e37.jpg[/img]
[b]STEP3:[/b]
调用addTwo方法,在调用该方法时,JVM创建一个新的frame,并将自己实例的指针,以及参数12和13传给新的Frame中的局部变量表。
注:每条invokevirtual指令都会带有一个表示索引的参数,如图中的#2, 通过该标识,JVM可以通过查找常量池找到方法addTwo的符号引用,该符号引用可以提供方法所在对象的类型的内部二进制名称、方法名称和方法描述符
[img]http://glutinit.iteye.com/upload/picture/pic/102566/c106b400-120c-33ea-b4fa-00f396402f10.jpg[/img]
[b]STEP4:[/b]
目前的操作都针对Frame_addTwo中的操作数栈,如图,从局部变量表加载位置的变量和位置2的变量,即12和13
[img]http://glutinit.iteye.com/upload/picture/pic/102562/8414e6dd-c3fb-38c5-9882-99dfcda29399.jpg[/img]
[b]STEP5:[/b]
求和运算,将结果25压入到操作数栈中
[img]http://glutinit.iteye.com/upload/picture/pic/102552/83b0144a-c551-3fb0-abb5-183a52756e3e.jpg[/img]
[b]STEP6:[/b]
addTwo()方法返回,将其栈顶的值(即25)push到其调用者(即add12And13)方法所在的操作数栈,并且在该调用完成后,addTwo所拥有的frame就被删除掉。
[img]http://glutinit.iteye.com/upload/picture/pic/102550/e522a2db-5989-39d7-a4b7-730c5251fb14.jpg[/img]
[b]STEP7:[/b]
add12And13()方法返回,将其栈顶的值(即25)push到其调用者所在的操作数栈,并且在该调用完成后,add12And13所拥有的frame就被删除掉。
[img]http://glutinit.iteye.com/upload/picture/pic/102560/95325818-a5c5-3a7b-ab5c-2bf4b3fbde09.jpg[/img]
毕竟在大多数编程中,JVM的知识不会直接的影响到你的编程思路,但会潜移默化的让你绕过一些潜在的风险或者障碍。[/color]
下面稍微介绍两个概念:[b]接收参数[/b] 和 [b]方法调用[/b]
[b][size=large]接收参数[/size][/b]
实例方法(非静态方法)和类方法(静态方法)的实现是略有不同的,因为当调用一个实例方法时,首先会将该类的自身的实例的引用作为第0个参数放置在局部变量表中,然后才在局部变量表的第1到N的位置上放置参数(如果有参数, N是参数个数)。
而静态方法不需要传递自身实例的应用,所以参数是从第0个开始存放。
(要注意long和double所占局部变量的大小为2)
大家可以用下面这两个例子做下测试
//example_1
int addTwo(int i, int j) {
return i + j;
}
//example_2, static method
static int addTwo(int i, int j)
{
return i + j;
}
[b][size=large]方法调用[/size][/b]
对普通实例方法(即非静态方法)调用是在运行时根据对象类型进行分派的(相当于在C++中所说的“虚方法”), 这类方法通过调用invokevirtual指令实现。
下面看一个简单的例子,然后是其字节码:
public class JVM_3_addTwo {
int addTwo(int i, int j)
{
return i + j;
}
int add12And13(){
return addTwo(12, 13);
}
}
...
int addTwo(int, int);
Code:
0: iload_1
1: iload_2
2: iadd
3: ireturn
int add12And13();
Code:
0: aload_0
1: bipush 12
3: bipush 13
5: invokevirtual #2; //Method addTwo:(II)I
8: ireturn
}
其具体流程如下(图只是用来参考,并不一定完全正确...)
[b]STEP1:[/b]
从局部变量表load当前类实例的指针,将其push到操作数栈
[img]http://glutinit.iteye.com/upload/picture/pic/102548/0fc7515c-86a6-390b-b92b-ed04fe591f44.jpg[/img]
[b]
STEP2:[/b]
将常量12和13分别push到操作数栈
[img]http://glutinit.iteye.com/upload/picture/pic/102558/a4938cdf-e39c-3a6e-a2f9-fd203ed08e37.jpg[/img]
[b]STEP3:[/b]
调用addTwo方法,在调用该方法时,JVM创建一个新的frame,并将自己实例的指针,以及参数12和13传给新的Frame中的局部变量表。
注:每条invokevirtual指令都会带有一个表示索引的参数,如图中的#2, 通过该标识,JVM可以通过查找常量池找到方法addTwo的符号引用,该符号引用可以提供方法所在对象的类型的内部二进制名称、方法名称和方法描述符
[img]http://glutinit.iteye.com/upload/picture/pic/102566/c106b400-120c-33ea-b4fa-00f396402f10.jpg[/img]
[b]STEP4:[/b]
目前的操作都针对Frame_addTwo中的操作数栈,如图,从局部变量表加载位置的变量和位置2的变量,即12和13
[img]http://glutinit.iteye.com/upload/picture/pic/102562/8414e6dd-c3fb-38c5-9882-99dfcda29399.jpg[/img]
[b]STEP5:[/b]
求和运算,将结果25压入到操作数栈中
[img]http://glutinit.iteye.com/upload/picture/pic/102552/83b0144a-c551-3fb0-abb5-183a52756e3e.jpg[/img]
[b]STEP6:[/b]
addTwo()方法返回,将其栈顶的值(即25)push到其调用者(即add12And13)方法所在的操作数栈,并且在该调用完成后,addTwo所拥有的frame就被删除掉。
[img]http://glutinit.iteye.com/upload/picture/pic/102550/e522a2db-5989-39d7-a4b7-730c5251fb14.jpg[/img]
[b]STEP7:[/b]
add12And13()方法返回,将其栈顶的值(即25)push到其调用者所在的操作数栈,并且在该调用完成后,add12And13所拥有的frame就被删除掉。
[img]http://glutinit.iteye.com/upload/picture/pic/102560/95325818-a5c5-3a7b-ab5c-2bf4b3fbde09.jpg[/img]