李林超博客
首页
归档
留言
友链
动态
关于
归档
留言
友链
动态
关于
首页
Java
正文
04.并发编程之线程运行原理
Leefs
2022-10-06 PM
520℃
0条
[TOC] ### 一、概念介绍 + **方法区**:是虚拟机中一块线程共享的内存区域,用于存储类信息、常量池、静态变量、编译后的字节码等。 + **堆**:是虚拟机中最大的一块线程共享的内存区域,堆是 Java 内存管理的核心区域,所有的对象实例和数组都在堆中分配内存。 + **虚拟机栈**:是线程私有的内存区域。虚拟机栈的内存空间是给线程使用的,每启动一个线程,虚拟机都为其分配一块栈内存空间,虚拟机栈中可以存在多个栈帧。 + **栈帧**:每个线程分配的虚拟机栈内存区域由多个栈帧(Frame)组成,栈帧对应着每个方法调用时所占用的内存;每个栈帧是由局部变量表、操作数栈、动态链接、方法返回值地址等组成。 ![04.并发编程之线程运行原理02.png](https://lilinchao.com/usr/uploads/2022/10/940360476.png) + **程序计数器**:是一块内存很小的线程私有的内存空间,每个线程都有自己的程序计数器。任何时间一个线程都只有一个方法在执行,程序计数器会记录当前执行方法中的 `JVM` 指令地址,用于控制程序的正确执行。程序的分支、跳转、循环、异常以及线程切换都需要依靠程序计数器来完成。 ### 二、栈与栈帧关系 **Java Virtual Machine Stacks (Java 虚拟机栈)** `JVM` 中由堆、栈、方法区所组成,其中栈内存是给线程使用的,每个线程启动后,虚拟机就会为其分配一块栈内存。 - 每个栈由多个栈帧(Frame)组成,对应着每次方法调用时所占用的内存; - 每个线程只能有一个活动栈帧,对应着当前正在执行的那个方法; - 每个栈帧是由局部变量表、操作数栈、动态链接、方法返回值地址等组成。 **虚拟机栈与栈帧的关系如下:** ![04.并发编程之线程运行原理01.png](https://lilinchao.com/usr/uploads/2022/10/439902596.png) ### 三、栈帧DEBUG #### 3.1 代码示例 ```java /** * Created by lilinchao * Date 2022/10/6 * Description 方法调用时栈帧变化 */ 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; } } ``` #### 3.2 DEBUG步骤详解 **(1)在`method1(10);`方法上打断点,然后通过Debug方式运行程序** ![04.并发编程之线程运行原理03.jpg](https://lilinchao.com/usr/uploads/2022/10/271207659.jpg) + Frames中包函所有的栈帧 + Variables中是每个栈帧中对应的局部变量 刚启动程序,只有一个main线程,main 方法的参数是一个String 数组,参数名称为`args`,此时 Variables 变量表中有一个 args={String[0]@620},数组对象的大小为 0。 **(2)通过`Step into`(快捷键F7)进入到静态方法`method1(10)`** ![04.并发编程之线程运行原理04.jpg](https://lilinchao.com/usr/uploads/2022/10/39482658.jpg) 此时线程栈栈帧表 Frames 中有两个栈帧,`method1` 的栈帧中有一个局部变量 `x=10`,局部变量初始值是从main方法中传递而来。 **(3)在继续`Step into`执行代码`int y = x + 1;`** ![04.并发编程之线程运行原理05.jpg](https://lilinchao.com/usr/uploads/2022/10/1136168418.jpg) 此时在栈帧`method1`中增加一个局部变量y,并被赋值为11 **(4)在继续`Step into`,程序进入到`method2()`方法** ![04.并发编程之线程运行原理06.jpg](https://lilinchao.com/usr/uploads/2022/10/3992359065.jpg) 此时Frames中增加一个栈帧`method2`,`method2` 的栈帧中有一个局部变量 n,该局部变量是在 `method2` 中实例化的 Object 对象。 *从这我们就可以看出,程序每调用一个方法,就会在栈帧表中多增加一个相应的栈帧* **(5)`Step into`继续向下执行** ![04.并发编程之线程运行原理07.jpg](https://lilinchao.com/usr/uploads/2022/10/98622065.jpg) `method1`中调用`method2`方法结束,此时 Frames 中只有 `method1` 和 main 方法两个栈帧,`method2` 方法由于运行结束方法返回后,就会弹栈(出栈)。并且`method2`方法将自身创建的Object对象作为参数返回给`method1`方法中的局部变量m。 **(6)接下来,程序执行`System.out.println(m);`在控制台输出相应信息** **(7)继续`Step into`,`method1`方法调用结束** ![04.并发编程之线程运行原理08.jpg](https://lilinchao.com/usr/uploads/2022/10/3431425408.jpg) 此时Frames 中只有main方法一个栈帧,`method1` 方法由于运行结束,就会弹栈(出栈) **(8)最后main方法运行结束出栈,程序运行结束** #### 3.3 总结 从上面通过一步步Debug查看代码执行过程可以得出以下两个结论: + 程序每调用一个方法,就会在栈帧表(Frames )中增加一个相应的栈帧; + 当方法运行结束方法返回后,就会弹栈(出栈)。 ### 四、栈帧图解 由与Debug的局限性,还是不能够深入的理解整个线程的运行流程,以及内部栈帧的组成,接下来,通过图解的方式,将上方示例代码运行过程在进行详细说明,方便对线程的运行流程进行理解。 **(1)类加载过程** ![04.并发编程之线程运行原理09.jpg](https://lilinchao.com/usr/uploads/2022/10/1495897163.jpg) + 执行类加载,将`TestFrames`类中的字节码加载到Java的虚拟机**方法区**的内存当中; + 为了便于理解,上图的方法区当中没有放入java类的字节码,而是放入了java的源代码; **(2)运行主线程** 当类加载完成之后,CPU会开始运行主线程当中的代码 ![04.并发编程之线程运行原理10.jpg](https://lilinchao.com/usr/uploads/2022/10/1900952466.jpg) + 最开始执行 main 函数,此时虚拟机栈中会为 main 线程分配一块栈内存供 main 线程运行(main 线程栈), main 线程栈中会压入一个 main 函数栈帧; + main 函数拥有一个 String[] args 局部变量,因此局部变量表中 args 指向一个堆中的 String 数组(局部变量表会在方法运行之前就创建完成,分配好内存); + main函数的返回地址就是程序退出地址,当程序运行结束会自动退出。 **(3)method1方法调用** ![04.并发编程之线程运行原理11.jpg](https://lilinchao.com/usr/uploads/2022/10/3493408125.jpg) + 主函数中调用`method1`方法,此时在main线程栈中会压入一个 `method1` 函数栈帧; + `method1` 函数中有三个局部变量,分别是 x、y、m, x 的值由方法传递已知,因此 x=10; + 向下执行程序`int y = x + 1;`,此时会将该行代码加载到程序计数器,局部变量y会被赋值为11; **(4)method2方法调用** ![04.并发编程之线程运行原理13.jpg](https://lilinchao.com/usr/uploads/2022/10/761071314.jpg) + 执行程序`Object m = method2();`,此时调用`method2`方法,将 `method2` 函数栈帧压入main线程栈当中; + 程序向下执行`method2`方法中的代码`Object n = new Object();`,将该行代码加载到程序计数器; + 此时在堆内存中创建了一个 Object 对象,并且将对象地址赋值给 n 引用。 **(5)执行method2中的return方法** ![04.并发编程之线程运行原理14.jpg](https://lilinchao.com/usr/uploads/2022/10/236487840.jpg) + 程序计数器加载程序`return n;`将Object对象的引用地址传递给m,此时m的引用地址指向堆中的Object对象; + `method2`调用结束,因此`method2` 函数栈帧将会从main线程栈中出栈; + 程序重新返回到`method1`方法m的位置; **(6)method1方法执行结束** ![04.并发编程之线程运行原理15.jpg](https://lilinchao.com/usr/uploads/2022/10/1941759375.jpg) + 当打印方法执行结束,此时method1中的方法也执行完毕,`method1` 函数栈帧从main线程栈中出栈; + 线程来到主函数。 **(7)主函数执行结束,出栈,程序执行完毕** ### 五、多线程执行逻辑 #### 5.1 示例代码 ```java /** * Created by lilinchao * Date 2022/10/6 * Description 多线程方法调用时栈帧变化 */ 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; } } ``` #### 5.2 执行步骤 **(1)分别在主线程和`t1线程`的`method1`方法上打断点** ![04.并发编程之线程运行原理16.jpg](https://lilinchao.com/usr/uploads/2022/10/373576558.jpg) **(2)通过Debug模式启动程序** ![04.并发编程之线程运行原理17.jpg](https://lilinchao.com/usr/uploads/2022/10/4221952650.jpg) + 在断点处鼠标右击,将程序的Debug模式由All切换成Thread,在点击Done确定修改 *两个断点处都进行上述修改* **(3)切换栈帧** ![04.并发编程之线程运行原理18.jpg](https://lilinchao.com/usr/uploads/2022/10/1131572291.jpg) + main线程和t1线程两个线程的栈帧都是相互独立的,通过上图的下拉框可以切换到相应的栈帧进行Debug运行 *剩下的部分和上文中的第三部分《栈帧DEBUG》相同,本篇将不在过多介绍* ### 六、线程上下文切换(Thread Context Switch) 因为以下一些原因导致 cpu 不再执行当前的线程,转而执行另一个线程的代码 - 线程的 cpu 时间片用完 - 垃圾回收 - 有更高优先级的线程需要运行 - 线程自己调用了 sleep、yield、wait、join、park、synchronized、lock 等方法 当 Context Switch 发生时,需要由操作系统保存当前线程的状态,并恢复另一个线程的状态,Java 中对应的概念就是程序计数器(Program Counter Register),它的作用是记住下一条 jvm 指令的执行地址,是线程私有的 - 状态包括程序计数器、虚拟机栈中每个栈帧的信息,如局部变量、操作数栈、返回地址等 - Context Switch 频繁发生会影响性能
标签:
并发编程
非特殊说明,本博所有文章均为博主原创。
如若转载,请注明出处:
https://lilinchao.com/archives/2458.html
上一篇
03.并发编程之线程的运行
下一篇
05.并发编程之线程常见方法
取消回复
评论啦~
提交评论
栏目分类
随笔
2
Java
326
大数据
229
工具
31
其它
25
GO
43
标签云
RSA加解密
稀疏数组
高并发
哈希表
Java阻塞队列
HDFS
Filter
锁
SpringCloud
递归
MyBatisX
人工智能
Zookeeper
ajax
DataX
nginx
国产数据库改造
查找
JavaWEB项目搭建
MyBatis-Plus
Shiro
Azkaban
Thymeleaf
设计模式
DataWarehouse
Scala
微服务
序列化和反序列化
Golang
MyBatis
友情链接
申请
范明明
庄严博客
Mx
陶小桃Blog
虫洞