多线程教程(三)多线程原理解析
我们都知道 JVM 中由堆、栈、方法区所组成,其中栈内存是给谁用的呢?其实就是线程,每个线程启动后,虚拟机就会为其分配一块栈内存。
-
每个栈由多个栈帧(Frame)组成,对应着每次方法调用时所占用的内存
-
每个线程只能有一个活动栈帧,对应着当前正在执行的那个方法
1.类加载

运行test类,java类的字节码会加载到虚拟机中方法区之中。(图片中方法区用代码代表字节码)
关于图片程序计数器在栈帧里面的思考,以及什么是线程栈
每个Java方法执行时都会创建一个栈帧到虚拟机栈中。
一个栈帧包括局部变量表,操作栈,动态链接,方法出口等信息。
写递归程序出错时时的栈溢出,就是发生在这里
- 实际上程序计数器不应该在栈帧里面
- 线程栈在jvm中也不存在,实际上这是linux中的概念
进程栈:
进程栈是属于用户态栈,和进程 虚拟地址空间 (Virtual Address Space) 密切相关。那我们先了解下什么是虚拟地址空间:在 32 位机器下,虚拟地址空间大小为 4G。这些虚拟地址通过页表 (Page Table) 映射到物理内存,页表由操作系统维护,并被处理器的内存管理单元 (MMU) 硬件引用。每个进程都拥有一套属于它自己的页表,因此对于每个进程而言都好像独享了整个虚拟地址空间。
生成main线程栈,为主方法生成栈帧内存

局部变量表对应着方法内的局部变量和方法参数

如上所属的代码,局部变量表中存储的就是局部变量,局部变量由虚拟机在堆上创建,并将引用地址传给局部变量
返回地址对应程序的退出地址
2.运行到指定方法

当运行到method1的时候,在线程栈中会创建method1的栈帧,局部变量表中存储着局部变量,局部变量的空间是在创建栈帧的时候就全部创建完毕。
method1的返回地址就是主方法method1之下的代码地址。
method1中调用method2,所以会创建一个新的method2栈帧。

3.指定方法运行完毕

method2方法执行完毕之后,method2方法的栈帧就会被释放掉,返回到返回地址的位置。

method1方法执行完毕之后,method1方法的栈帧也会被释放掉,当main方法执行完毕之后,main栈帧也会被释放掉。
4.当两个线程同时运行的时候
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;
}
}
当两个线程同时运行的时候,两个线程并不干扰,分别生成自己的栈帧,分别运行。
**线程上下文切换(**Thread Context Switch)
因为以下一些原因导致 cpu 不再执行当前的线程,转而执行另一个线程的代码
线程的 cpu 时间片用完
垃圾回收
有更高优先级的线程需要运行
线程自己调用了 sleep、yield、wait、join、park、synchronized、lock 等方法
当 Context Switch 发生时,需要由操作系统保存当前线程的状态,并恢复另一个线程的状态,Java 中对应的概念就是程序计数器(Program Counter Register),它的作用是记住下一条 jvm 指令的执行地址,是线程私有的
状态包括程序计数器、虚拟机栈中每个栈帧的信息,如局部变量、操作数栈、返回地址等
Context Switch 频繁发生会影响性能

参考文章:
https://www.jianshu.com/p/44adf6b71f8f
nshu.com/p/44adf6b71f8f
本文深入探讨Java多线程中的线程栈和栈帧原理,每个线程拥有独立的栈内存,栈帧随着方法调用创建和销毁。局部变量表、操作栈和返回地址是栈帧的重要组成部分。线程上下文切换涉及程序计数器、栈帧等状态保存与恢复,频繁切换会影响性能。文中还通过实例展示了两个线程如何并发执行,并解释了上下文切换的原因和影响。
351

被折叠的 条评论
为什么被折叠?



