Java Virtual Machine Stacks (Java 虚拟机栈 JVM)
我们都知道 JVM 中由堆、栈、方法区所组成,其中栈内存是给谁用的呢?其实就是线程,每个线程启动后,虚拟机就会为其分配一块栈内存。
每个栈由多个栈帧(Frame)组成,对应着每次方法调用时所占用的内存
每个线程只能有一个活动栈帧,对应着当前正在执行的那个方法
单线程示例代码
public class TestFrames { public static void main(String[] args) { method1(10); // 断点处 } private static void method1(int x) { int y = x + 1; Object m = method2(); System.out.println(m); } private static Object method2() { Object n = new Object(); return n; } }
在打断点处,可以看到一个栈帧
执行到method1,可以看到新起了一个栈帧
当执行到method2时,可以看到又新起了一个栈帧
由于是栈,随着的程序的运行,后面开启的栈帧会先被销毁,直至main栈帧被销毁,此刻程序运行完成。
对应图解:
内存释放后
具体就是:
1.将编译好的字节码加载到jvm的方法区内存中2.jvm启动一个main的主线程,cpu核心就准备运行主线程的代码了,给主线程分配自己的栈内存【args、局部变量、返回地址、所记录】,每个线程的栈里面还有个程序计数器程序计数器的作用:当cpu要执行哪行代码了,就去这个里面去要3.把主方法的里面代码行放到程序计数器4.主方法调用的是method1的方法,为method1分配栈内存,里面存储这个方法里面局部变量,返回地址,这些变量是分配内存时,会把空间预留好5.将method1的第一行读到程序计数器让cpu执行6.methode1下一行调用method2()方法,创建他的栈内存7.把Object n = new Object()这行代码读取到计数器,在队中创建对象8.method2()将返回地址给m,方法执行完就可以释放掉method2()的栈内存9.一层层方法结束后,依次释放掉每个方法线程
现在来看看多线程下的栈与栈帧
public class TestFrames { public static void main(String[] args) { Thread t1 = new Thread(){ @Override public void run() { method1(20);// 断点处 } }; t1.setName("t1"); t1.start(); method1(10);// 断点处 } private static void method1(int x) { int y = x + 1; Object m = method2(); System.out.println(m); } private static Object method2() { Object n = new Object(); return n; } }
在第一个断点处
可以看到多个线程同时运行中,我们可以选择具体的线程来查看运行状况并且往下运行,具体的读者可以自行实践。
因为以下一些原因导致 cpu 不再执行当前的线程,转而执行另一个线程的代码(简单来说就是从使用cpu到不使用cpu)
线程的 cpu 时间片用完
垃圾回收
有更高优先级的线程需要运行
线程自己调用了 sleep、yield、wait、join、park、synchronized、lock 等方法
当 Context Switch(上下文切换) 发生时,需要由操作系统保存当前线程的状态,并恢复另一个线程的状态,Java 中对应的概念就是程序计数器(Program Counter Register),它的作用是记住下一条 jvm 指令的执行地址,是线程私有的
状态包括程序计数器、虚拟机栈中每个栈帧的信息,如局部变量、操作数栈、返回地址等