前面研究了字节码结构,类加载的过程,现在了解,字节码执行。
一、运行时栈帧结构
1.局部变量表
①局部变量表必须赋初始值
②如何存储和占用内存大小
使用slot存储,根据数据类型有使用1个slot的类型,也有2个slot的类型的。
引起线程安全的三个条件:
① 多线程
②共享资源
③共享资源进行非原子性操作
③slot可以复用
代码演示:
public class GCDemo { public static void main(String[] args) { { byte[] buff = new byte[60 * 1024 * 1024]; } //int a = 10; System.gc(); } }
打开注释代码: int a = 10;
原因:
2.操作数栈
①定义和工作过程
Java虚拟机的解释执行引擎被称为"基于栈的执行引擎",其中所指的栈就是指-操作数栈。
操作数栈也常被称为操作栈。
和局部变量区一样,操作数栈也是被组织成一个以字长为单位的数组。但是和前者不同的是,它不是通过索引来访问,而是通过标准的栈操作—压栈和出栈—来访问的。比如,如果某个指令把一个值压入到操作数栈中,稍后另一个指令就可以弹出这个值来使用。
虚拟机在操作数栈中存储数据的方式和在局部变量区中是一样的:如int、long、float、double、reference和returnType的存储。对于byte、short以及char类型的值在压入到操作数栈之前,也会被转换为int。
虚拟机把操作数栈作为它的工作区——大多数指令都要从这里弹出数据,执行运算,然后把结果压回操作数栈。比如,iadd指令就要从操作数栈中弹出两个整数,执行加法运算,其结果又压回到操作数栈中,看看下面的示例,它演示了虚拟机是如何把两个int类型的局部变量相加,再把结果保存到第三个局部变量的:
begin iload_0 // push the int in local variable 0 onto the stack iload_1 // push the int in local variable 1 onto the stack iadd // pop two ints, add them, push result istore_2 // pop int, store into local variable 2 end
在这个字节码序列里,前两个指令iload_0和iload_1将存储在局部变量中索引为0和1的整数压入操作数栈中,其后iadd指令从操作数栈中弹出那两个整数相加,再将结果压入操作数栈。第四条指令istore_2则从操作数栈中弹出结果,并把它存储到局部变量区索引为2的位置。下图详细表述了这个过程中局部变量和操作数栈的状态变化,图中没有使用的局部变量区和操作数栈区域以空白表示。
②操作数栈共享区域
3.动态链接
在Class文件中的常量持中存有大量的符号引用。字节码中的方法调用指令就以常量池中指向方法的符号引用作为参数。这些符号引用一部分在类的加载阶段(解析)或第一次使用的时候就转化为了直接引用(指向数据所存地址的指针或句柄等),这种转化称为静态链接。而相反的,另一部分在运行期间转化为直接引用,就称为动态链接。
与那些在编译时进行链接的语言不同,Java类型的加载和链接过程都是在运行的时候进行的,这样虽然在类加载的时候稍微增加一些性能开销,但是却能为Java应用程序提供高度的灵活性,Java中天生可以动态扩展的语言特性就是依赖动态加载和动态链接这个特点实现的。
动态扩展就是在运行期可以动态修改字节码,也就是反射机制与cglib。
4.方法返回地址
两种退出方式
5.附加信息
二、方法调用
方法调用
1.解析调用
以下四种情况不能重载或者从写:
也就对应下面的指令:
看下解析调用已经确定好了。演示如下:
public class Hello { public static void sayHello(){ System.out.println("hello world"); } public static void main(String[] args) { Hello.sayHello(); } }
编译后,执行javap -verbose Hello.class.
可以看到main方法中的invokestatic指令指定了sayHello,方法已经确定,不可修改,这就是方法调用解析:
2.分派调用
①静态分派调用
静态分派也是在编译阶段就可确定。
例子:
public class Demo { static abstract class Human{ } static class Man extends Human{ } static class Woman extends Human{ } public void sayHello(Human guy){ System.out.println("hello,guy"); } public void sayHello(Man guy){ System.out.println("hello,gentleman"); } public void sayHello(Woman guy){ System.out.println("hello,lady"); } public static void main(String[] args) { Human man = new Man(); Human woman = new Woman(); Demo demo = new Demo(); demo.sayHello(man); demo.sayHello(woman); } }
运行结果:
why?
执行javap -verbose Dmeo.class再来验证下:看下
②动态分派调用
例子:
public class Demo { static abstract class Human { protected abstract void sayHello(); } static class Man extends Human { @Override protected void sayHello() { System.out.println("man say hello"); } } static class WoMan extends Human { @Override protected void sayHello() { System.out.println("woman say hello"); } } public static void main(String[] args) { Human man = new Man(); Human woMan = new WoMan(); man.sayHello(); woMan.sayHello(); man = new WoMan(); man.sayHello(); } }
运行结果:
why?
三、动态语言支持
java8以后引入新的js引擎,而JS就是动态类型的语言。说明java也支持了动态语言,当然java8之前就有js引擎了。
完