禅与计算机 禅与计算机
首页
  • Java基础

    • 一文搞懂Java核心技术
    • Java面向对象知识点大总结,建议收藏
    • 聊聊Java中的异常
    • 聊聊Java中的常用类String
    • 万字长文带你细聊Java注解本质
    • 来聊聊Java的反射机制
    • 深入解析Java泛型的魅力与机制
    • Java集合框架深度解析与面试指南
    • Java常用集合类HashMap深度解析
    • LinkedHashMap源码到面试题的全解析
    • 深入解析CopyOnWriteArrayList的工作机制
    • Java基础IO总结
    • Java三大IO模型小结
    • Java BIO NIO AIO详解
    • Java进阶NIO之IO多路复用详解
    • Java8流式编程入门
    • 一文速通lambda与函数式编程
    • Java8函数式方法引用最佳实践
    • Java异常:从原理到实践
  • Java并发编程

    • Java并发编程基础小结
    • 深入理解Java中的final关键字
    • 浅谈Java并发安全发布技术
    • 浅谈Java并发编程中断的哲学
    • Java线程池知识点小结
    • 浅谈Java线程池中拒绝策略与流控的艺术
    • synchronized关键字使用指南
    • 深入源码解析synchronized关键字
    • 详解JUC包下的锁
    • 详解并发编程中的CAS原子类
    • LongAdder源码分析
    • AQS源码解析
    • 深入剖析Java并发编程中的死锁问题
    • Java并发容器总结
    • 详解Java并发编程volatile关键字
    • 并发编程ThreadLocal必知必会
    • CompletableFuture基础实践小结
    • CompletableFuture异步多任务最佳实践
    • 硬核详解FutureTask设计与实现
    • 线程池大小设置的底层逻辑与场景化方案
    • 来聊一个有趣的限流器RateLimiter
  • JVM相关

    • 从零开始掌握 JVM
    • JVM核心知识点小结
    • JVM指令集概览:基础与应用
    • JVM类加载器深度解析
    • JVM方法区深度解析
    • Java内存模型JMM详解
    • Java对象大小的精确计算方法
    • 逃逸分析在Java中的应用与优化
    • 从零开始理解JVM的JIT编译机制
    • G1垃圾回收器:原理详解与调优指南
    • JVM故障排查实战指南
    • JVM内存问题排错最佳实践
    • JVM内存溢出排查指南
    • 简明的Arthas使用教程
    • 简明的Arthas配置及基础运维教程
    • 基于Arthas Idea的JVM故障排查与指令生成
    • 基于arthas量化监控诊断java应用方法论与实践
    • 深入剖析arthas技术原理
    • 探索JVM的隐秘角落:元空间详解
  • 深入理解Spring框架

    • Spring 核心知识点全面解析
    • Spring核心功能IOC详解
    • Spring AOP 深度剖析与实践
    • Spring 三级缓存机制深度解析
    • 深入 Spring 源码,剖析设计模式的落地实践
    • 探索 Spring 事务的奥秘
    • 深入解析Spring Bean的生命周期管理
    • 解读 Spring Boot 核心知识点
    • Spring Boot 启动优化实战:1分钟到13秒的排查与优化之路
    • Spring Boot自动装配原理及实践
    • 一文快速上手Sharding-JDBC
    • sharding-jdbc如何实现分页查询
    • 基于DynamicDataSource整合分库分表框架Shardingsphere
  • 计算机组成原理

    • 计算机硬件知识小结
    • CPU核心知识点小结
    • 浅谈CPU流水线的艺术
    • 从Java程序员视角聊聊CPU缓存
    • CPU任务调度和伪共享问题小结
    • CPU MESI缓存一致性协议
    • CPU内存管理机制
    • 内存深度解析
    • 磁盘存储原理
    • 详解计算机启动步骤
    • CPU南北桥架构与发展史
    • CPU中断机制与硬件交互详解
  • 操作系统

    • 如何实现一个高性能服务器
    • Linux文件结构与文件权限
    • Linux常见压缩指令小结
    • Linux核心系统调用详解
    • Linux进程管理
    • Linux线程管理
    • 进程与线程深度解析
    • Linux进程间通信机制
    • 零拷贝技术原理与实践
    • CPU缓存一致性问题深度解析
    • IO任务与CPU调度艺术
  • 计算机网络

    • 网卡通信原理详解
    • 网卡数据包处理指南
    • 基于抓包详解TCP协议
  • 编码最佳实践

    • 浅谈现代软件工程TDD最佳实践
    • 浅谈TDD模式下并发程序设计与实现
    • 面向AI编程新范式Trae后端开发环境搭建与实践
    • 基于提示词工程的Redis签到功能开发实践
    • 基于Vibe Coding的Redis分页查询实现
    • 告别AI无效对话:资深工程师的提示词设计最佳实践
  • 实用技巧与配置

    • Mac常用快捷键与效率插件指南
    • Keynote技术科普短视频制作全攻略
  • 元认知

    • 摩擦感:AI时代的写作自省
    • 从断墨寻径浅谈程序员的元学习能力
    • AI时代专注力培养
    • 如何阅读一本书:技术书籍的读书笔记方法论
  • 开发工具

    • IDEA配置详解与高效使用指南
  • Nodejs
  • 博客搭建
  • Redis

    • Redis核心知识小结
    • 解锁Redis发布订阅模式
    • 掌握Redis事务
    • Redis主从复制技术
    • Redis的哨兵模式详解
    • 深度剖析Redisson分布式锁
    • 详解redis单线程设计思路
    • 来聊聊Redis所实现的Reactor模型
    • Redis RDB持久化源码深度解析
    • 来聊聊redis的AOF写入
    • 来聊聊Redis持久化AOF管道通信的设计
    • 来聊聊redis集群数据迁移
    • Redis SDS动态字符串深度解析
    • 高效索引的秘密:redis跳表设计与实现
    • 聊聊redis中的字典设计与实现
  • MySQL

    • MySQL基础知识点小结
    • 解读MySQL 索引基础
    • MySQL 索引进阶指南
    • 解读MySQL Explain关键字
    • 探秘 MySQL 锁:原理与实践
    • 详解MySQL重做日志redolog
    • 详解undoLog在MySQL MVCC中的运用
    • MySQL二进制日志binlog核心知识点
    • MySQL高效插入数据的最佳实践
    • MySQL分页查询优化指南
    • MySQL流式查询的奥秘与应用解析
    • 来聊聊分库分表
    • 来聊聊大厂常用的分布式ID生成方案
  • ElasticSearch

    • Elasticsearch核心原理与架构设计
    • ES 基础使用指南
    • ElasticSearch如何写入一篇文档
    • 深入剖析Elasticsearch文档读取原理
    • 聊聊ElasticSearch性能调优
    • Spring借助Easy-Es操作ES
  • Netty

    • 一文快速了解高性能网络通信框架Netty
    • Netty网络传输简记
    • 来聊聊Netty的ByteBuf
    • 来聊聊Netty消息发送的那些事
    • 解密Netty高性能之谜:NioEventLoop线程池阻塞分析
    • 详解Netty中的责任链Pipeline如何管理ChannelHandler
    • Netty Reactor模型常见知识点小结
    • Netty如何驾驭TCP流式传输?粘包拆包问题全解
    • Netty解码器源码解析
  • 消息队列

    • 一文快速入门消息队列
    • 消息队列RocketMQ入门指南
    • 基于RocketMQ实现分布式事务
    • RocketMQ容器化最佳实践
    • RocketMQ常见问题与深度解析
    • Kafka快速安装与使用指南
  • Nginx

    • Linux下的nginx安装
    • Nginx基础入门总结
    • Nginx核心指令小结
    • Nginx进程结构与核心模块初探
    • Nginx应用进阶HTTP核心模块配置
    • Nginx缓存及HTTPS配置小记
    • nginx高可用实践简记
    • Nginx性能优化
  • 微服务基础

    • 微服务基础知识小结
    • 分布式事务核心概念小结
    • OpenFeign核心知识小结
    • 微服务组件Gateway核心使用小结
    • 分布式事务Seata实践
    • 用 Docker Compose 完成 Seata 的整合部署
  • Nacos

    • Nacos服务注册原理全解析
    • Nacos服务订阅流程全解析
    • Nacos服务变更推送流程全解析
    • 深入解析SpringCloud负载均衡器Loadbalancer
    • Nacos源码环境搭建与调试指南
  • Seata

    • 深度剖析Seata源码
  • Docker部署

    • 一文快速掌握docker的理念和基本使用
    • 使用docker编排容器
    • 基于docker-compose部署微服务基本环境
    • 基于docker容器化部署微服务
    • Gateway全局异常处理及请求响应监控
    • Docker图形化界面工具Portainer最佳实践
  • Go基础

    • 一文带你速通Go语言基础语法
    • 一文快速掌握Go语言切片
    • 来聊聊go语言的hashMap
    • 一文速通go语言类型系统
    • 浅谈Go语言中的面向对象
    • go语言是如何实现协程的
    • 聊聊go语言中的GMP模型
    • 极简的go语言channel入门
    • 聊聊go语言基于epoll的网络并发实现
    • 写给Java开发的Go语言协程实践
  • mini-redis实战

    • 来聊聊我用go手写redis这件事
    • mini-redis如何解析处理客户端请求
    • 实现mini-redis字符串操作
    • 硬核复刻redis底层双向链表核心实现
    • 动手复刻redis之go语言下的字典的设计与落地
    • Go 语言下的 Redis 跳表设计与实现
    • Go 语言版 Redis 有序集合指令复刻探索
  • 项目编排

    • Spring脚手架创建简记
    • Spring脚手架集成分页插件
    • Spring脚手架集成校验框架
    • maven父子模块两种搭建方式简记
    • SpringBoot+Vue3前后端快速整合入门
    • 来聊聊Java项目分层规范
  • 场景设计

    • Java实现文件分片上传
    • 基于时间缓存优化浏览器轮询阻塞问题
    • 基于EasyExcel实现高效导出
    • 10亿数据高效插入MySQL最佳方案
    • 从开源框架中学习那些实用的位运算技巧
  • CI/CD

    • 基于NETAPP实现内网穿透
    • 基于Gitee实现Jenkins自动化部署SpringBoot项目
    • Jenkins离线安装部署教程简记
    • 基于Nexus搭建Maven私服基础入门
    • 基于内网的Jenkins整合gitlab综合方案简记
  • 监控方法论

    • SpringBoot集成Prometheus与Grafana监控
    • Java监控度量Micrometer全解析
    • 从 micrometer计量器角度快速上手promQL
    • 硬核安利一个监控告警开源项目Nightingale
  • Spring AI

    • Spring AI Alibaba深度实战:一文掌握智能体开发全流程
    • Spring AI Alibaba实战:JVM监控诊断Arthas Agent的工程化构建与最佳实践
  • 大模型评测

    • M2.7 真能打!我用两个真实场景测了测,结果有点意外
    • Qoder JetBrains插件评测:祖传代码重构与接口优化实战
  • AI工具链

    • Claude Code 实战指南:从安装配置到企业级开发流程
    • 一次 Claude Code 启动失败的 AI 辅助排查复盘
    • Claude Code 记忆管理:CLAUDE.md 最佳实践
关于
收藏
  • 分类
  • 标签
  • 归档
GitHub (opens new window)

sharkchili

计算机禅修者
首页
  • Java基础

    • 一文搞懂Java核心技术
    • Java面向对象知识点大总结,建议收藏
    • 聊聊Java中的异常
    • 聊聊Java中的常用类String
    • 万字长文带你细聊Java注解本质
    • 来聊聊Java的反射机制
    • 深入解析Java泛型的魅力与机制
    • Java集合框架深度解析与面试指南
    • Java常用集合类HashMap深度解析
    • LinkedHashMap源码到面试题的全解析
    • 深入解析CopyOnWriteArrayList的工作机制
    • Java基础IO总结
    • Java三大IO模型小结
    • Java BIO NIO AIO详解
    • Java进阶NIO之IO多路复用详解
    • Java8流式编程入门
    • 一文速通lambda与函数式编程
    • Java8函数式方法引用最佳实践
    • Java异常:从原理到实践
  • Java并发编程

    • Java并发编程基础小结
    • 深入理解Java中的final关键字
    • 浅谈Java并发安全发布技术
    • 浅谈Java并发编程中断的哲学
    • Java线程池知识点小结
    • 浅谈Java线程池中拒绝策略与流控的艺术
    • synchronized关键字使用指南
    • 深入源码解析synchronized关键字
    • 详解JUC包下的锁
    • 详解并发编程中的CAS原子类
    • LongAdder源码分析
    • AQS源码解析
    • 深入剖析Java并发编程中的死锁问题
    • Java并发容器总结
    • 详解Java并发编程volatile关键字
    • 并发编程ThreadLocal必知必会
    • CompletableFuture基础实践小结
    • CompletableFuture异步多任务最佳实践
    • 硬核详解FutureTask设计与实现
    • 线程池大小设置的底层逻辑与场景化方案
    • 来聊一个有趣的限流器RateLimiter
  • JVM相关

    • 从零开始掌握 JVM
    • JVM核心知识点小结
    • JVM指令集概览:基础与应用
    • JVM类加载器深度解析
    • JVM方法区深度解析
    • Java内存模型JMM详解
    • Java对象大小的精确计算方法
    • 逃逸分析在Java中的应用与优化
    • 从零开始理解JVM的JIT编译机制
    • G1垃圾回收器:原理详解与调优指南
    • JVM故障排查实战指南
    • JVM内存问题排错最佳实践
    • JVM内存溢出排查指南
    • 简明的Arthas使用教程
    • 简明的Arthas配置及基础运维教程
    • 基于Arthas Idea的JVM故障排查与指令生成
    • 基于arthas量化监控诊断java应用方法论与实践
    • 深入剖析arthas技术原理
    • 探索JVM的隐秘角落:元空间详解
  • 深入理解Spring框架

    • Spring 核心知识点全面解析
    • Spring核心功能IOC详解
    • Spring AOP 深度剖析与实践
    • Spring 三级缓存机制深度解析
    • 深入 Spring 源码,剖析设计模式的落地实践
    • 探索 Spring 事务的奥秘
    • 深入解析Spring Bean的生命周期管理
    • 解读 Spring Boot 核心知识点
    • Spring Boot 启动优化实战:1分钟到13秒的排查与优化之路
    • Spring Boot自动装配原理及实践
    • 一文快速上手Sharding-JDBC
    • sharding-jdbc如何实现分页查询
    • 基于DynamicDataSource整合分库分表框架Shardingsphere
  • 计算机组成原理

    • 计算机硬件知识小结
    • CPU核心知识点小结
    • 浅谈CPU流水线的艺术
    • 从Java程序员视角聊聊CPU缓存
    • CPU任务调度和伪共享问题小结
    • CPU MESI缓存一致性协议
    • CPU内存管理机制
    • 内存深度解析
    • 磁盘存储原理
    • 详解计算机启动步骤
    • CPU南北桥架构与发展史
    • CPU中断机制与硬件交互详解
  • 操作系统

    • 如何实现一个高性能服务器
    • Linux文件结构与文件权限
    • Linux常见压缩指令小结
    • Linux核心系统调用详解
    • Linux进程管理
    • Linux线程管理
    • 进程与线程深度解析
    • Linux进程间通信机制
    • 零拷贝技术原理与实践
    • CPU缓存一致性问题深度解析
    • IO任务与CPU调度艺术
  • 计算机网络

    • 网卡通信原理详解
    • 网卡数据包处理指南
    • 基于抓包详解TCP协议
  • 编码最佳实践

    • 浅谈现代软件工程TDD最佳实践
    • 浅谈TDD模式下并发程序设计与实现
    • 面向AI编程新范式Trae后端开发环境搭建与实践
    • 基于提示词工程的Redis签到功能开发实践
    • 基于Vibe Coding的Redis分页查询实现
    • 告别AI无效对话:资深工程师的提示词设计最佳实践
  • 实用技巧与配置

    • Mac常用快捷键与效率插件指南
    • Keynote技术科普短视频制作全攻略
  • 元认知

    • 摩擦感:AI时代的写作自省
    • 从断墨寻径浅谈程序员的元学习能力
    • AI时代专注力培养
    • 如何阅读一本书:技术书籍的读书笔记方法论
  • 开发工具

    • IDEA配置详解与高效使用指南
  • Nodejs
  • 博客搭建
  • Redis

    • Redis核心知识小结
    • 解锁Redis发布订阅模式
    • 掌握Redis事务
    • Redis主从复制技术
    • Redis的哨兵模式详解
    • 深度剖析Redisson分布式锁
    • 详解redis单线程设计思路
    • 来聊聊Redis所实现的Reactor模型
    • Redis RDB持久化源码深度解析
    • 来聊聊redis的AOF写入
    • 来聊聊Redis持久化AOF管道通信的设计
    • 来聊聊redis集群数据迁移
    • Redis SDS动态字符串深度解析
    • 高效索引的秘密:redis跳表设计与实现
    • 聊聊redis中的字典设计与实现
  • MySQL

    • MySQL基础知识点小结
    • 解读MySQL 索引基础
    • MySQL 索引进阶指南
    • 解读MySQL Explain关键字
    • 探秘 MySQL 锁:原理与实践
    • 详解MySQL重做日志redolog
    • 详解undoLog在MySQL MVCC中的运用
    • MySQL二进制日志binlog核心知识点
    • MySQL高效插入数据的最佳实践
    • MySQL分页查询优化指南
    • MySQL流式查询的奥秘与应用解析
    • 来聊聊分库分表
    • 来聊聊大厂常用的分布式ID生成方案
  • ElasticSearch

    • Elasticsearch核心原理与架构设计
    • ES 基础使用指南
    • ElasticSearch如何写入一篇文档
    • 深入剖析Elasticsearch文档读取原理
    • 聊聊ElasticSearch性能调优
    • Spring借助Easy-Es操作ES
  • Netty

    • 一文快速了解高性能网络通信框架Netty
    • Netty网络传输简记
    • 来聊聊Netty的ByteBuf
    • 来聊聊Netty消息发送的那些事
    • 解密Netty高性能之谜:NioEventLoop线程池阻塞分析
    • 详解Netty中的责任链Pipeline如何管理ChannelHandler
    • Netty Reactor模型常见知识点小结
    • Netty如何驾驭TCP流式传输?粘包拆包问题全解
    • Netty解码器源码解析
  • 消息队列

    • 一文快速入门消息队列
    • 消息队列RocketMQ入门指南
    • 基于RocketMQ实现分布式事务
    • RocketMQ容器化最佳实践
    • RocketMQ常见问题与深度解析
    • Kafka快速安装与使用指南
  • Nginx

    • Linux下的nginx安装
    • Nginx基础入门总结
    • Nginx核心指令小结
    • Nginx进程结构与核心模块初探
    • Nginx应用进阶HTTP核心模块配置
    • Nginx缓存及HTTPS配置小记
    • nginx高可用实践简记
    • Nginx性能优化
  • 微服务基础

    • 微服务基础知识小结
    • 分布式事务核心概念小结
    • OpenFeign核心知识小结
    • 微服务组件Gateway核心使用小结
    • 分布式事务Seata实践
    • 用 Docker Compose 完成 Seata 的整合部署
  • Nacos

    • Nacos服务注册原理全解析
    • Nacos服务订阅流程全解析
    • Nacos服务变更推送流程全解析
    • 深入解析SpringCloud负载均衡器Loadbalancer
    • Nacos源码环境搭建与调试指南
  • Seata

    • 深度剖析Seata源码
  • Docker部署

    • 一文快速掌握docker的理念和基本使用
    • 使用docker编排容器
    • 基于docker-compose部署微服务基本环境
    • 基于docker容器化部署微服务
    • Gateway全局异常处理及请求响应监控
    • Docker图形化界面工具Portainer最佳实践
  • Go基础

    • 一文带你速通Go语言基础语法
    • 一文快速掌握Go语言切片
    • 来聊聊go语言的hashMap
    • 一文速通go语言类型系统
    • 浅谈Go语言中的面向对象
    • go语言是如何实现协程的
    • 聊聊go语言中的GMP模型
    • 极简的go语言channel入门
    • 聊聊go语言基于epoll的网络并发实现
    • 写给Java开发的Go语言协程实践
  • mini-redis实战

    • 来聊聊我用go手写redis这件事
    • mini-redis如何解析处理客户端请求
    • 实现mini-redis字符串操作
    • 硬核复刻redis底层双向链表核心实现
    • 动手复刻redis之go语言下的字典的设计与落地
    • Go 语言下的 Redis 跳表设计与实现
    • Go 语言版 Redis 有序集合指令复刻探索
  • 项目编排

    • Spring脚手架创建简记
    • Spring脚手架集成分页插件
    • Spring脚手架集成校验框架
    • maven父子模块两种搭建方式简记
    • SpringBoot+Vue3前后端快速整合入门
    • 来聊聊Java项目分层规范
  • 场景设计

    • Java实现文件分片上传
    • 基于时间缓存优化浏览器轮询阻塞问题
    • 基于EasyExcel实现高效导出
    • 10亿数据高效插入MySQL最佳方案
    • 从开源框架中学习那些实用的位运算技巧
  • CI/CD

    • 基于NETAPP实现内网穿透
    • 基于Gitee实现Jenkins自动化部署SpringBoot项目
    • Jenkins离线安装部署教程简记
    • 基于Nexus搭建Maven私服基础入门
    • 基于内网的Jenkins整合gitlab综合方案简记
  • 监控方法论

    • SpringBoot集成Prometheus与Grafana监控
    • Java监控度量Micrometer全解析
    • 从 micrometer计量器角度快速上手promQL
    • 硬核安利一个监控告警开源项目Nightingale
  • Spring AI

    • Spring AI Alibaba深度实战:一文掌握智能体开发全流程
    • Spring AI Alibaba实战:JVM监控诊断Arthas Agent的工程化构建与最佳实践
  • 大模型评测

    • M2.7 真能打!我用两个真实场景测了测,结果有点意外
    • Qoder JetBrains插件评测:祖传代码重构与接口优化实战
  • AI工具链

    • Claude Code 实战指南:从安装配置到企业级开发流程
    • 一次 Claude Code 启动失败的 AI 辅助排查复盘
    • Claude Code 记忆管理:CLAUDE.md 最佳实践
关于
收藏
  • 分类
  • 标签
  • 归档
GitHub (opens new window)
  • 计算机组成原理

  • 操作系统

    • 如何实现一个高性能服务器
    • Linux文件结构与文件权限
    • Linux常见压缩指令小结
    • 浅谈Linux权限管理
    • Linux核心系统调用详解
    • Linux进程管理
    • Linux线程管理
    • 进程与线程深度解析
    • Linux进程间通信机制
    • 浅谈Linux基于信号处理中断的哲学
    • 从操作系统底层浅谈程序栈的高效性
      • 写在文章开头
      • 前置知识铺垫
        • 执行栈或程序栈的基本概念
        • 详解操作系统内存管理
        • 内存也可能是性能瓶颈的罪魁祸首
      • 详解程序执行栈执行高效的奥秘
        • 合理空间预分配
        • cpu寄存器对于栈指针的灵活把控
        • 局部性友好
      • 栈的局限性
        • 不可动态扩容
        • 线程隔离
      • 基于栈上分配技术说明栈的实际性能表现
      • 小结
      • 参考
    • 零拷贝技术原理与实践
    • CPU缓存一致性问题深度解析
    • IO任务与CPU调度艺术
    • 来聊聊函数回调
    • 一个完美主义者的自我救赎
  • 计算机网络

  • 运维

  • 编码最佳实践

  • 计算机基础
  • 操作系统
sharkchili
2026-01-07
目录

从操作系统底层浅谈程序栈的高效性

# 写在文章开头

写于2025.12.19,随着ai的不断发展,编程成为一种思想上的抽象,开发者将更专注于软件架构设计与功能抽象本身,技术或许将会成为一种"黑盒"的技术手段,当然笔者这里说的"黑盒"需要打上引号,因为笔者强调的黑盒并非是说明开发者无需关注技术实现的内核,而是强调开发者无需过分关注技术底层,只有在必要时需要针对性的研究技术本身的特性,从而更好的在软件系统这个不断进行封装和抽象的世界推进。

而本文也是一篇关于元认知的文章,即操作系统实现层面,让读者了解编程语言底层的工作机制,了解栈的工作原理,相信通过这篇文章,读者将会对编程技术有着更深层次的理解和掌握。

你好,我是 SharkChili ,禅与计算机程序设计艺术布道者,希望我的理念对您有所启发。

📝 我的公众号:写代码的SharkChili
在这里,我会分享技术干货、编程思考与开源项目实践。

🚀 我的开源项目:mini-redis
一个用于教学理解的 Redis 精简实现,欢迎 Star & Contribute:
https://github.com/shark-ctrl/mini-redis (opens new window)

👥 欢迎加入读者群
关注公众号,回复 【加群】 即可获取联系方式,期待与你交流技术、共同成长!

# 前置知识铺垫

# 执行栈或程序栈的基本概念

学习过数据结构的同学想必都知道,栈是后进先出的数据结构,即先压入栈中的元素最后才会弹出,注意这里笔者所说压入和弹出都是逻辑上的概念,栈的底层本质上还是用数组来表达,通过数组结合指针的游走表达栈元素的压入和弹出。

而本文笔者要聊的并不是栈这个数据结构,而是程序中的执行栈,它的核心工作原理也是运用到数据结构上的栈,每当涉及变量分配或者函数调用时,都会将相应的方法或者变量压入栈中,处理完成后弹出并销毁。

例如我们现在有下面这样一段代码,即声明两个整形变量之后,通过add方法将其相加然后返回运算结果:

func main() {
	num1 := 1
	num2 := 2
	sum := add(num1, num2)
	println(sum)
}

func add(num1 int, num2 int) int {
	return num1 + num2
}
1
2
3
4
5
6
7
8
9
10

对应的栈的执行过程如下:

  1. 将main方法压入栈中
  2. 将num1和num2的数值都压入栈中
  3. 触发add函数调用将add方法压入栈中
  4. 完成运算结果,add返回栈指针复位,回到add调用前的地址

对应执行流程如下图,可以看到栈可以在编译时预分配指定大小空间以保证程序可以正确的写入和执行,理想情况下合适大小的内存空间可以保证:

  1. 栈执行的性能表现出色(后文会展开说明)
  2. 内存利用率高,不会因为不连续的内存空间使用造成内存碎片

# 详解操作系统内存管理

上文提到了一些关于内存碎片的一点概念,鉴于本文关于栈的执行性能表现需要一定的计算机理论基础,所以笔者也在这里聊一些关于内存方面的知识。上述笔者提到内存利用率不高这一问题,假定我们程序没有合理连续的使用内存造成预分配内存空间不足的情况。 实际上,这种情况程序是可以向操作系统再申请内存空间的,按照《现代操作系统》一书中的说法,一个进程大体范围如下三个部分:

  1. 代码段:代码段即可执行的物理代码,对应相同逻辑的多进程是共享的,所以内存空间分配固定
  2. 数据段:它存在用户初始化的值和非初始化的值,操作系统为了减少非必要的内存资源浪费,在进程初始化时仅会针对代码段和存在初始化值(例如全局变量num=1)分配内存空间,针对未初始化的变量则是将这些变量指向一个零页面的内存空间,等到真正使用的时候再通过malloc函数申请分配内存空间
  3. 栈:每个进程对应的线程线程独享,栈段执行永远是从虚拟地址的0xc0000000或其附近的地址开始向低地址不断延伸(将函数或者变量入栈、弹出完成运算任务)

如果我们在预分配指定大小的栈空间中进行非连续的写入,就会造成所分配的内存空间不足,此时,程序就需要向操作系统申请内存空间,这就涉及操作系统的内存管理问题了。

我们以操作系统最经典的伙伴算法为例,进行内存分配时永远是基于2的次幂进行分配,假设我们现在有64个页面,与此同时出现两个分别发起8、4页面内存请求,那么64个页面就会从从低地址开始进行等分切割。即:

  1. 将64个页面切分为32个页面
  2. 低位的32再切分为16个页面
  3. 低位的16切分为2个8个页面
  4. 低位的8分配给进程1个页面
  5. 低位的另一个8的页面切割为4,分配给进程2

如下图伙伴算法正是通过这种不断二分的算法管理内存分配,完成后就会将内存空间释放返回:

以伙伴算法为例,因为2的次幂申请和其内存管理的理念,尝尝会出现大量内存碎片问题,最经典的场景就是为了65大小的页面请求,到内存中寻找128大小的页面。所以如果大量的进程没有合理的使用内存空间,按照现代操作系统的内存管理理念,如果内存空间不足,则会将某些进程交换至虚拟内存空间,也就是所谓的swap分区,对应我们可以通过linux的free指令查看,需要了解的是这块空间本质上就是磁盘空间,其读写性能表现相对于内存而言要逊色许多:

[root@iv-xxxx ~]# free  -m
               total        used        free      shared  buff/cache   available
Mem:            1782         635         199          40        1215        1147
Swap:              0           0           0
[root@iv-xxxx ~]#
1
2
3
4
5

这也是为什么比较本文已经强调的要学会通过当前需求获取合理紧凑的内存空间,而这也就是为什么栈的执行相较于堆要高效的原因之一。

# 内存也可能是性能瓶颈的罪魁祸首

考虑到CPU时钟周期与内存一个维度上(CPU访问速度大约2-4个时钟周期,内存大约是200-300个时钟周期),如果一味指望完成内存读写后,CPU在着手处理读写任务,这就很可能出现CPU大量时钟周期被浪费,也就是我们常说的空转问题。所以设计者也在CPU内部增加了一层由SRAM电路构成的缓存来减少CPU对于内存的访问,从而提升CPU处理效率以及减少CPU空转的概率。

对应cpu默认L1缓存行大小可以通过如下指令查看:

cat /sys/devices/system/cpu/cpu0/cache/index0/coherency_line_size
1

以笔者的服务器为例,对应第0个CPU的缓存行大小大约是64字节:

需要补充说明的是每个CPU核心的一级缓存行缓存都是独享的,所以处理操作系统在进行相同数据读写时本质都是通过总线通知和缓存一致性协议来确保数据一致性,由于缓存一致性协议涉及的内容较多,且不属于本文的范畴,这里笔者也仅仅简要说明一下大体的思路,整体来说CPU缓存一致性协议的原理就是相同缓存数据涉及最新的修改操作都需要进行如下操作:

  1. CPU-0读取的数据不在缓存行,则检查总线是否忙碌,若不忙碌则发起控制信号,等待存储器将目标数据放到总线上读取
  2. CPU-0触发修改操作,通过总线通知其它CPU核心这个写操作
  3. 其它CPU缓存收到通知后,查看各自数据是否标记为脏,若为脏则会将数据写入内存,并通过总线通知写操作的CPU-0操作数据为过期数据
  4. CPU-0收到通知后,拉取最新数据完成写操作

所以要想提升程序的执行性能,我们也可以从这个角度来考虑,即确保将本次需要用到的数据尽可能缓存到CPU缓存行,尽可能在当前CPU缓存行中完成数据读取和并基于CPU高速缓存完成目标代码对应的逻辑操作。

# 详解程序执行栈执行高效的奥秘

# 合理空间预分配

实际上,栈在操作系统中就是一块连续且紧凑的内存空间,以java为例,我们可以通过jinfo -flag ThreadStackSize [pid] 查看每个线程对应的栈大小,以笔者的系统为例,可以看到对应java进程的默认栈大小为2MB左右,这个大小相对于日常被的函数执行已经是非常冗余了,毕竟我们的日常编码都是以字节为单位在函数内操作各种字符串、整数、小数作为成员变量的类对象:

➜  ~ jinfo -flag ThreadStackSize 88954
-XX:ThreadStackSize=2048
1
2

# cpu寄存器对于栈指针的灵活把控

上文提及,程序执行对应的栈段都以虚拟地址的0xc0000000或其附近开始执行,这也就是栈基址的位置,程序的执行本质就是从栈基址开始不断将函数或者变量压入弹出的过程。现代CPU考虑到栈的空间连续且紧凑,结合编程语言中对于类型声明(int、long、byte)保证空间推进的可预测性,CPU内部专门有一块寄存器stack register维护执行栈的栈顶位置,所以在信息栈的逻辑压栈或者出栈等操作时,完全可以根据变量类型确定偏移量,通过指针快速移动定位,这一点相比于在堆内存申请的对象操作要简单高效许多。

如下图所示,执行完byte数据分配后,CPU看到当前程序需要声明一个4字节的函数,本质上就是通过栈指针定位栈地址向前推进4字节的空间将变量压入,相比于堆内存中malloc申请中申请各种算法以及可能存在的内存空间不足造成segmentation fault异常,栈明显是高效且可靠很多:

与之同时,相比于堆内存或是通过垃圾回收器或者开发者手动释放内存空间,栈中变量的生命周期随着函数的终止而直接消亡,栈可直接通过出栈快速完成内存空间,这一点也是栈执行高效的原因之一。

# 局部性友好

结合上文提及的CPU缓存行,以及栈天然良好空间连续分配和紧凑的数据结构,CPU操作栈上变量和函数时,基本可以做到一次预加载拉取到目标数据及其对应相邻存储单元变量,例如下面这段代码,按照缓存行64字节为单元,我们拉取num1时就会连带将num2拉取到缓存行,这使得CPU缓存行有非常大的概率在高速缓存行上完成程序运算:

func main() {
	num1 := 1
	num2 := 2
	sum := add(num1, num2)
	println(sum)
}

func add(num1 int, num2 int) int {
	return num1 + num2
}

1
2
3
4
5
6
7
8
9
10
11

对应我们也给出栈的连续空间和CPU缓存行紧密配合下发挥局部性原理的最大威力示意图:

# 栈的局限性

# 不可动态扩容

栈空间随着编译固定分配,无法动态根据变换而申请内存空间,同时因为stack的空间固定,如果递归逻辑没有明确写好结束终止条件,可能会将栈空间耗尽造成stack overflow。

# 线程隔离

栈内部的变量只有栈独享,对于并发场景下可以通过栈封闭保证线程安全,但是对于需要并发共享变量的场景则是捉襟见肘。

# 基于栈上分配技术说明栈的实际性能表现

因为java是笔者主力开发语言,对于栈的性能执行表现就从java这门语言的角度出发,以jdk8及更高版本为例,默认都是开启逃逸分析的,对应参数为(-XX:+DoEscapeAnalysis),通俗来说该参数一旦发现创建的对象并没有被外部线程访问,即没有逃逸或者方法逃逸(可被外部方法访问到)的情况下,对象就会在栈上分配。

对于下面这段代码而言,创建的TestObj并没有发生线程逃逸,符合栈上分配的标准:

public static void main(String[] args) {
        long start = System.currentTimeMillis();
        for (int i = 0; i < Integer.MAX_VALUE; i++) {
            new TestObj();
        }
        long end = System.currentTimeMillis();
        System.out.println("耗时:" + (end - start) + "ms");

        ThreadUtil.sleep(Integer.MAX_VALUE);
    }

    static class TestObj {

    }

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

所以我们可以在添加-XX:+PrintGC后,将程序启动,可以发现在最终耗时在4ms左右,对应gc次数为0,与之对应通过-XX:-DoEscapeAnalysis -XX:+PrintGC关闭逃逸分析使对象分配在堆内存上,再打印gc情况,对应输出结果如下,可以看到,不仅gc次数频繁,且方法执行耗时猛涨:

由此,不难看出,利用好程序栈的特性,不仅可以提升程序的性能表现,还能显著降低系统资源开销,由此是动态内存分配和gc回收这两块大头。

# 小结

利用好栈天然紧凑的内存结构,编写良好可达到栈上分配的面向对象程序让编译器组织好栈内数据,从而减少内存碎片,这种做法不仅可以提升其对象创建和销毁的性能表现,还能够利用好CPU缓存达到局部性加载以减少CPU对内存的访问从而提升程序性能,希望这个思路对你有所启发。

你好,我是 SharkChili ,禅与计算机程序设计艺术布道者,希望我的理念对您有所启发。

📝 我的公众号:写代码的SharkChili
在这里,我会分享技术干货、编程思考与开源项目实践。

🚀 我的开源项目:mini-redis
一个用于教学理解的 Redis 精简实现,欢迎 Star & Contribute:
https://github.com/shark-ctrl/mini-redis (opens new window)

👥 欢迎加入读者群
关注公众号,回复 【加群】 即可获取联系方式,期待与你交流技术、共同成长!

# 参考

【双语视界】栈凭什么这么快?底层原理超硬核解析! :https://www.bilibili.com/video/BV1HMTfzqEyP/?buvid=Y942A991A168E31D4296B54F9D22E6B8C782&from_spmid=united.player-video-detail.drama-float.0&is_story_h5=false&mid=3URzpfGDRykbWghI4MoSYg%3D%3D&plat_id=116&share_from=ugc&share_medium=iphone&share_plat=ios&share_session_id=43748391-DA2C-47EA-8188-1B774F1DA58E&share_source=WEIXIN&share_tag=s_i&spmid=united.player-video-detail.0.0&timestamp=1758041562&unique_k=x7Bgxk0&up_id=372185561&vd_source=bf04f9a485aa892c0242fbfdfca25589 (opens new window)

栈与堆的性能对比:深入探讨栈为何通常比堆更快 :https://blog.csdn.net/weixin_67120523/article/details/145305406 (opens new window)

java 如何查看最大栈空间 :https://docs.pingcode.com/baike/395110 (opens new window)

局部性原理:https://baike.baidu.com/item/局部性原理/3334556 (opens new window)

深入理解Java内存与运行时机制:逃逸分析、栈上分配与标量替换 :https://cloud.tencent.com/developer/article/2560474 (opens new window)

《深入理解Java虚拟机 (第3版)》

编辑 (opens new window)
上次更新: 2026/03/26, 01:05:31
浅谈Linux基于信号处理中断的哲学
零拷贝技术原理与实践

← 浅谈Linux基于信号处理中断的哲学 零拷贝技术原理与实践→

最近更新
01
Claude Code 记忆管理:CLAUDE.md 最佳实践
04-24
02
Claude Code 实战指南:从安装配置到企业级开发流程
04-20
03
一次 Claude Code 启动失败的 AI 辅助排查复盘
04-18
更多文章>
Theme by Vdoing | Copyright © 2025-2026 Evan Xu | MIT License | 桂ICP备2024034950号 | 桂公网安备45142202000030
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式
×
×