李林超博客
首页
归档
留言
友链
动态
关于
归档
留言
友链
动态
关于
首页
Java
正文
04.并发编程之线程运行原理
Leefs
2022-10-06 PM
1142℃
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
47
NLP
4
标签云
Spark RDD
Netty
Sentinel
Http
Java
Scala
Java工具类
Hbase
Docker
Spark Streaming
算法
数据结构和算法
Stream流
MyBatisX
链表
二叉树
SQL练习题
并发编程
国产数据库改造
高并发
ClickHouse
随笔
Beego
工具
Spring
SpringBoot
FastDFS
Elasticsearch
锁
散列
友情链接
申请
范明明
庄严博客
Mx
陶小桃Blog
虫洞
评论已关闭