随笔 程序运行的基本原理

程序是如何运行,又是如何崩溃的?

正如标题所言,今天我们来聊聊程序是如何执行的?以及又是如何崩溃的?我们哼哧哼哧写的代码并不是程序,本质上不过是一个文本文件。即便我们将我们写的代码通过编译生成的可执行文件,本质也没有变,最多不过是一个优点特殊的文本文件。如果要程序真正的运行起来,就需要操作系统将我们的可执行文件加载到内存中,除此之外,操作系统还会额外的创建堆空间,栈空间以及供操作系统管理需要的数据结构,比如PCB。完成了上述的过程,我们写的代码才能被叫做程序,也就是我们熟知的进程。一个进程占用的内存主要分为4个部分:

  1. 代码空间

  2. 堆空间

  3. 栈空间

  4. 相关的数据结构需要的空间

在程序运行的过程中,内存占用不断变化的空间有两个一个是堆空间,一个是栈空间。那么两者有何不同呢?

两者的共同点就是都是用来存储程序运行过程中产生的临时数据的,只不过数据的生命周期不同罢了。在堆空间中,数据的生命周期是可以跨函数的,一般用于共享的场景,关于堆内存的管理,不同的语言有不同的管理策略,比如,C语言对堆内存的管理完全交给程序员自己管理,Java对堆内存的管理是交由垃圾回收器管理。而栈空间的数据是无法跨函数的,可见范围只能在一个函数内部,典型的代表就是我们熟知的局部变量。针对栈空间的管理,是基于一定的规则的,栈这种数据结构有一个特点,先进后出。每一次函数调用,就会在栈空间中创建一个栈帧,这个栈帧里面存放的数据就是当前这个函数中用到的局部变量。当一个函数执行完毕,就会将对应的栈帧弹出。由于栈的特点,就保证了正在执行的程序的栈帧总是位于栈顶。

上述描述的不过是,进程静态的样子,接下来描述进程如何被操作系统调度。进程由三种状态:

  1. 就绪态

  2. 运行态

  3. 阻塞态

当进程处于就绪态时,意味着一切准备就绪,只需要等待操作系统的调度就可以执行。运行态就是进程正在使用CPU执行指令,阻塞态就是因为需要等待某些资源,可能是IO,可能是锁,而暂时不被操作系统调度,一旦等待的资源就绪,就会进入就绪态,这个时候就可以被操作系统调度。

不过现代的操作系统,调度单元可不是进程,而是线程,但是线程不过是共享了大部分数据的进程而已。也常常被别人称为轻量级进程。具体怎么回事呢?创建一个线程,其实就是创建一个进程,但是我们知道创建进程需要分配代码空间,堆空间,栈空间,以及相关的数据结构需要的空间。而线程做的事情是复用父进程的代码空间,堆空间,创建一个额外的栈空间,以及供操作系统管理的线程相关的数据结构 -- TCB。从这个角度看,我们的线程确实要比进程轻量一些。

讲完了程序是如何运行的,我们来看看程序会因为什么原因导致程序崩溃。主要有两个原因,一个原因是自己写的代码有BUG,一种就是计算机资源不够。当代码有Bug时,当CPU执行了这段有代码的Bug时,可能立即崩溃。也可能因为堆内存的管理不当,导致堆内存不断扩大,达到操作系统的限制,导致程序崩溃,这种情况叫做内存泄漏。计算机的资源总是有限的,比如内存只有那么多,当我们的程序运行过程中,使用的内存超过了限制,也会导致程序崩溃。

相信通过这部分比较浅显的介绍你一定知道了程序是如何运行的,以及程序是如何崩溃的了,但是这段描述不够细节,如果你需要更深入的学习,可以看看《程序员的自我修养 -- 链接、装载与库》

相关推荐
Hui Baby9 小时前
springAi+MCP三种
java
hsjcjh10 小时前
【MySQL】C# 连接MySQL
java
敖正炀10 小时前
LinkedBlockingDeque详解
java
wangyadong31710 小时前
datagrip 链接mysql 报错
java
untE EADO10 小时前
Tomcat的server.xml配置详解
xml·java·tomcat
ictI CABL10 小时前
Tomcat 乱码问题彻底解决
java·tomcat
敖正炀10 小时前
DelayQueue 详解
java
敖正炀10 小时前
PriorityBlockingQueue 详解
java
shark222222210 小时前
Spring 的三种注入方式?
java·数据库·spring
陈煜的博客11 小时前
idea 项目只编译不打包,跳过测试,快速开发
java·ide·intellij-idea