前言
栈是线程运行的单位,那接下我们不会讲太多概念性的东西,主要是通过Math.java这个类和大家分析学习。
一、线程栈分析
我们看下Math.java类:
public class Math {public static int initData = 666;public int compute(){//int a = 1;int b = 2;int c = (a+b)*10;return c;}public static void main(String[] args) {Math math = new Math();int rs = pute();System.out.println(rs);}}
由于main方法是主线程,JVM会为每个线程都分配一个栈的内存空间,一个方法对应一块栈帧内存区域。
首先为main()方法分配一块栈帧内存区域,入栈:
当执行到pute()的时候,为compute()方法分配一块栈帧内存区域:
在每个栈帧中有这么几个概念:局部变量、操作数栈、动态链接、方法出口。
我们以compute()方法中的代码进行讲解:
public int compute(){//一个方法对应一块栈帧内存区域int a = 1;int b = 2;int c = (a+b)*10;return c;}
这里先看两个概念局部变量和操作数栈,这也是两块内存空间。
在变量的内存空间第一个位置的指向是this。
接下来我们借助一个指令来进行学习:javap -c Math.class
这是将二进制反编译为更可读的jvm指令码,我们看下compute()的代码:
这指令码是什么意思呢?
这里整理了一个对应表:
/ynoteshare1/index.html?id=fd46a7566fe897abb8db5b25fa0accdf&type=note
Code 0:iconst_1 : 将int类型常量1压入操作数栈
Code 1:istore_1 : 将栈顶int型数值存入第二个本地变量
说明:int型数值1从操作数栈出栈,然后存如第二个本地变量,此时a=1;
Code 2: iconst_2 : 将int型(2)推送至栈顶
Code 3: istore_2 : 将栈顶int型数值存入第三个本地变量
到这里我们了解下程序计数器,对于内存模型不是独立的,需要整体理解,我们简单理解程序计算器就是代码执行的行号(实际上应该是内存的句柄或者指针),对于程序计算器也是一块内存空间,是有字节码执行引擎进行操作的。
Code 4: iload_1:将第二个int型本地变量推送至栈顶
Code 5: iload_2 : 将第三个int型本地变量推送至栈顶
Code 6: iadd :将栈顶两int型数值相加并将结果压入栈顶
将操作数栈的值拿出来进行iadd计算,得到3,在压入栈顶:
Code 7: bipush 10 : 将单字节的常量值(-128~127)推送至栈顶,这里就是10:
Code 9: imul : 将栈顶两int型数值相乘并将结果压入栈顶:
将操作数栈的两个数值取出进行imul计算得到30,并且压入栈顶:
Code 10: istore_3 : 将栈顶int型数值存入第四个本地变量
Code 11: iload_3 : 将第四个int型本地变量推送至栈顶。
这里就是将局部变量数值30推送操作数栈栈顶。
Code 12: ireturn : //从当前方法返回int
就是讲栈顶的数值取出进行返回。
接下来我们看下动态链接和方法出口:
动态链接比较复杂,我们之后在讲,我们看下方法出口,方法出口存储的就是方法执行完成之后需要返回的那个位置:
分析我们的代码方法出口就是指向了int rs = pute()这个地址了。
接下来我们看下main()方法的math局部变量,这个一个引用类型变量,那么它是这样子的: