首先,jvm的整体结构:
图中3个步骤对应(了解即可):
1、类装载子系统将编译后的class文件加载到运行时数据区(也就是大家常说的堆、栈这些区域)
2、把对应的相关代码按照Java的指令进行各个区域的内存分配
3、进行相关代码的运行
重点关注运行时数据区,这个是我们jvm调优的依据(为了方便理解,以一个例子来分析):
例:
public class a{
public static void main(String[] args){
int a = 0;
int b = 0;
tt();
}
public void tt(){
int a = 1;
int b = 2;
int num3 = a+b;
}
}
栈(数据结构就是栈结构,FILO:fisrt in last out,先进后出)
当一个线程启动的时候,栈内存都会为这个线程开辟一块单独的区域来供这个线程使用。举例:假如现在有线程1、线程2两个线程进行了下图的操作:
那么,此时栈内存会为线程1、线程2分配两块内存空间:
分配好的空间中又是什么结构呢?我们继续引出“栈帧”这个概念,栈帧其实就是每个线程在运行具体方法的时候所拥有的独立的内存空间,进而保证各自局部变量的作用域范围。如图:
可以看到,栈帧将main()方法和tt()方法各自的局部变量都打包在栈帧内,所以这就是为什么变量名同样叫a,b,main()的局部变量a,b能和tt()的局部变量a,b区分开来。然后我们继续深入栈帧的结构,栈帧除了上述的局部变量(严格应该叫局部变量表),还有操作数栈、动态链接、方法出口。如下图:
局部变量表和操作数栈:
结合代码说明一下局部变量表和操作数栈(数据结构也是栈结构)的操作过程(具体的操作过程可以使用javap进行反编译后根据jvm指令来查看整体代码运行过程),
public void tt(){
int a = 1;
int b = 2;
int num3 = a+b;
}
int a = 1:
局部变量分配空间给a(int a,int变量初始值为0,a此刻等于0)->操作数栈将数字1出栈->1赋值给a(a = 1)
int b = 2:
局部变量分配空间给b(int b,int变量初始值为0,b此刻等于0)->操作数栈将数字2出栈->1赋值给b(b = 2)
int num3 = a+b:
局部变量分配空间给num3(int num3,int变量初始值为0,num3此刻等于0)->操作数栈将a+b(即1+2)的计算结果数字3出栈
->3赋值给num3(num3 = 3)
动态链接:
(暂时没完全理解)略~
方法出口:
其实就是记录方法执行完后应回到哪里的作用。例如:
方法出口就是记录线程在运行b()方法后需要回到a()方法的位置,进而执行a()方法的剩余代码。