JVM完成类的加载并产生Class对象以后,就可以执行Class对象的静态方法或者实例化对象调用其方法。JVM在源码编译阶段将源码编译为JVM字节码,JVM字节码是一种中间代码,JVM会在运行期执行该中间代码,这种方式成为字节码的解释执行方式。
字节码的解释执行:
如下我们写出这样一个简单的类文件:
public class Demo {
public void execute() {
Teacher.teach();
Teacher teacher = new Teacher();
teacher.run();
People student = new Student();
student.run();
}
}
interface People {
public int run();
}
class Teacher {
public static int teach() {
return 1 + 2;
}
public int run () {
return 1+2;
}
}
class Student implements People {
public int run () {
return 1 + 2;
}
}
使用命令javac Demo.java编译上面的类,然后使用javap -c Demo查看该类方法execute方法生成的字节码文件如下:
public void execute();
Code:
0: invokestatic #2; //Method Teacher.teach:()I
3: pop
4: new #3; //class Teacher
7: dup
8: invokespecial #4; //Method Teacher."<init>":()V
11: astore_1
12: aload_1
13: invokevirtual #5; //Method Teacher.run:()I
16: pop
17: new #6; //class Student
20: dup
21: invokespecial #7; //Method Student."<init>":()V
24: astore_2
25: aload_2
26: invokeinterface #8, 1; //InterfaceMethod People.run:()I
31: pop
32: return
}
JVM采用四种指令来执行不同的方法调用:invokestatic调用static方法,invokevirtual调用对象实例方法,invokeinteface调用的是接口方法,invokespecial调用的是private方法和编译源码后生成的init方法,如构造方法。
SUN JDK基于stack的体系结构来执行字节码,基于stack能是代码更加紧凑,体积更小。stack的系统结构如下图:
线程在创建后都会产生pc registers和stack;pc存放了下一条将要执行的指令在方法内的偏移量;stack中存放了StackFrame,每个方法每次调用都会产生StackFrame,StackFrame又主要分为局部变量区和操作数栈,局部变量区主要存放方法中的局部变量和参数,操作数栈主要存放方法在执行过程中产生的中间变量;下面我们具体来看一段代码的执行指令:代码很简单,如下:
public class Demo {
public static void execute() {
int a = 1;
int b = 2;
int c = (a + b) * 4;
}
}
以下为该类的完成字节码:
public class Demo extends java.lang.Object{
public Demo();
Code:
0: aload_0
1: invokespecial #1; //Method java/lang/Object."<init>":()V
4: return
public static void execute();
Code:
0: iconst_1 // 将类型为int,值为1的常量放入操作数栈中
1: istore_0 //将操作数栈中栈顶的值放入局部变量区
2: iconst_2 //将类型为int,值为2的常量放入操作数栈中
3: istore_1 //将操作数栈栈顶的值放入局部变量区
4: iload_0 //装载局部变量区的第一个值到操作数栈
5: iload_1 //装载局部变量区的第二个值到操作数栈
6: iadd //执行int整数的加法操作,并将结果存入操作数栈中
7: iconst_4 //将类型为int,值为4的常量放入操作数栈中
8: imul //执行乘法操作,并将结果存入操作数栈中
9: istore_2 //将操作数栈中栈顶的值存入局部变量区
10: return //返回
}
我们主要看该execute方法下的code,指令的解释详见指令后面的注释。