并发编程——1.java内存图及相关内容

这篇文章,我们来讲一下java的内存图及并发编程的预备内容。

首先,我们来看一下下面的这两段代码:

下面,我们给出上面这两段代码在运行时的内存结构图,如下图所示:

下面,我们来具体的讲解一下。

首先,我们写了一个java程序是以.java的文件形式保存在磁盘中的,当我们运行它的时候,首先,jdk会将其编译为.class文件,用的是javac命令,也是在磁盘中,然后,jre会去运行.class文件,用的是java命令,然后,我们的程序就被运行了,这是整个过程。

当我们需要去运行一个java程序时,或者说,当jdk用javac命令去编译一个.java文件的时候,操作系统就会在内存中开辟一片区域,叫java运行时内存,里面存储我们一个java程序在运行时的所有信息。

java运行时内存中会有方法区,里面存放的是我们类的信息,包括类的成员变量和成员方法。方法区中还有一块叫做静态方法区,里面存放的是我们类中的静态方法。方法区中的内容在类被编译的时候就会生成,生成之后会将类中的静态方法拷贝一份到静态方法区。注意,java中的所有静态资源在类被编译的时候都会被初始化。根据代码我们可以知道,person类中只有m4是静态方法,它会被拷贝到静态方法区中,Test1中所有的方法都是静态方法,都会被拷贝到静态方法区中。

之后会有一块栈区,它是控制方法的执行顺序及变量的定义域。程序是从main方法开始执行的,所以首先main方法入栈,然后main方法中创建了3个person类的实例,即x1,x2,x3,所以java会在堆内存中创建三个实例对象,这三个实例对象会存储类的一切信息,除了静态方法。然后main方法调用m1方法,m1方法入栈,m1方法调用m2方法,m2方法入栈,m2方法调用m3方法,m3方法入栈,m3方法中创建person类的实例x1,所以java在堆内存中创建出实例对象,然后m3方法执行实例x1的m2方法,所以m3方法中的实例x1的m2方法入栈,而该实例的m2方法又调用该类的m1方法,所以该实例的m1方法入栈,等m1执行结束后,x1的m1方法出栈,然后x1的m2出栈,然后Test1的m3出栈,然后Test1的m2出栈,然后Test1的m1出栈,然后回到main方法中再依次往下执行。这就是一个java程序执行的整个流程。

以上的内存模型只是一种简略内存模型,更详细的内存模型大家可以去参考我的JVM系列内容。

上面只是单个线程的,下面来看一下多线程的。

看一下下面的这段代码:

看一下输出结果:

我们结合上面单线程的内存分布,来分析一下这段代码的内存分布:

代码的编译和方法区就不说了,直接从主方法开始说。

代码是从主方法开始运行的,运行主方法的时候,java会在栈区中开辟出主线程栈,然后主方法入栈,执行,执行到第5行的时候,代码new了一个新的线程x1,所以在堆中创建出线程x1的实例,这个实例非常复杂,但是我们可以将他简化为里面有start和run两个方法。创建完成之后,同时,在栈区中创建新的线程x1,在线程x1中,方法run拷贝入栈,准备执行,同时,主线程栈中的代码也会执行,所以就有我们看到的运行结果:线程x1和主线程交替着打印输出。

这样解释可能不好理解,下面换个角度解释一下。

这是我电脑的部分CPU的部分性能信息,我们来看下面的几行信息。

进程,进程的科学定义是:进程是程序在某个数据集合上的一次运行活动,也是操作系统进行资源分配和保护的基本单位 。通俗的说:进程就是程序的一次执行过程。进程数213,就是说,当前时刻,我的电脑上有213个程序处于"运行"状态(或者说开着)。

线程,**线程是CPU调度的最小单位,**简单来说,CPU每次只能运行线程,不能运行进程。线程数3190,就是说当前时刻,我的电脑上一共有3190个线程,并且这3190个线程是分布在213个进程中的(进程是由线程组成的)

句柄,就是变量,句柄107879,就是说当前时刻,我的电脑内存中一个包含107879个变量。

内核,就是CPU的核数,一个CPU有多少核数,那么这个CPU在同一时刻就能执行多少个线程。我的CPU是8核的,说明我的电脑在同一时刻能跑8个线程。

CPU的核数是一定的,线程数是变化的,并且线程数是远远大于核数的,CPU每次只能执行8个线程,那么剩下的线程就只能暂时处于其他状态(这个操作系统中有介绍),但是一台电脑不可能只靠这8个线程来运行,所以CPU是在不停的做线程切换的,也就是说CPU每个线程执行一段时间然后就切换去执行另一个线程,这就是多线程。

明白了这点,我们再看上面的代码,那是两个线程,主线程和x1线程,他们两个竞争者进入CPU,然后被CPU执行,当某个线程被CPU选中时,那个这个线程中的内容就会被执行,但是它不是直接执行完的,而是会有线程的切换,当它被切换出去了,它就不会被执行了,也就不会被打印输出了。这就是上面交替输出的原因。

前面说了线程的切换,线程的状态等内容,这些是操作系统中的,这里只是简单的提到,如果想要具体的了解,可以参考我的操作系统专栏中的内容。

这篇文章比较散,就是讲了一下一个java程序在运行时的内存结构图,然后稍微的提到了多线程。这些都是比较基础的,都是要好好掌握。

相关推荐
非优秀程序员5 小时前
第04章 邮箱大战,163 与 QQ 的暗战|《互联网产品恩仇录》
java·vue.js·架构
知已难寻6 小时前
Docker 入门:容器技术与 Docker 基础
java·运维·服务器·docker·云原生·容器
沐晓时光6 小时前
C语言入门,内存函数知识讲解
c语言·开发语言
文丑颜不良啊8 小时前
LeetCode2481~2500题目解析
java·leetcode
Flynn Liang9 小时前
同一需求三套写法:用 Spring AI、LangChain4j、AgentScope Java 各实现一个带工具调用的 RAG 助手(实战对比)
java·人工智能·大模型·架构设计·spring ai·langchain4j·agentscope
聪明的笨猪猪9 小时前
实际工程问题:AI 生成 Spring Cache(@Cacheable / @CachePut / @CacheEvict)缓存注解踩坑
java·经验分享·面试·ai编程
愿有所息10 小时前
记一次 ARM64 + JDK8 上 fastjson2 的诡异数据错乱
java
秋饼10 小时前
MCP 2026-07-28 无状态协议大改版:Spring AI 2.1 多轮往返请求与网关路由落地
java·ai·技术分享·后端开发
SL_staff11 小时前
JVS-BI 实战:业务人员如何用零代码 ELT + 可视化配置构建实时经营分析闭环
java·spring·全栈
SL_staff12 小时前
企业AI落地难?JVS-AI渐进式实践:安全可控、小切口验证、标准化沉淀与规模化复制
java·开源·aigc