那个“栈”经常会在各种文章里看到。
先不管专业定义,先看一下外行也能稍微理解一下的意思。
stack和heap都是放东西,stack可以看到一层一层的,heap就是一堆团在一起。
👩🍳大厨在家做饭,要用到【厨房台面】和【冰箱/储物柜】。
stack(栈):大厨手边的料理台工作区
特点!
· 用完即走
· 只放当前步骤要用的东西
· 顺序明确,越新的东西越放在“最上层”
· 空间有限,挤满了就不好用了
· 任务结束,台面会自动恢复空白
heap(堆):大厨的冰箱、放干货的储物柜等等。
特点!
· 容量大、可以随便塞
· 用不用无所谓,可以放很久
· 不按顺序,随便放
· 要靠大厨手动整理,否则会越来越乱
· 不会自动清理,除非你开冰箱清空
从冰箱取肉|从heap取数据放到料理台|在stack上创建临时变量切肉、炒肉|运行函数,使用stack用完的剩下食材放回冰箱|把需要长期存活的对象保存在heap做完饭把料理台清空|函数结束,stack自动清理冰箱里还留着没用完的东西|heap的对象继续存在,等待 GC 或释放例句 1:函数里放太多东西,stack 会爆
“别在函数里定义 20MB 的数组,stack 会直接爆掉。”
场景: 某人写了一个函数,里面搞了个巨大的临时数组,程序直接崩。
真实原因:stack 空间固定(比如只有 1MB),装不下。
⸻
例句 2:这个变量别放 stack 上,生命周期太短
“你要返回这个对象,不能放 stack,它出了函数就没了。”
场景: 新手把一个结构体声明在函数里,还 return 它的地址。 运行结果:指针指向的内存瞬间消失。
stack 变量 = 函数结束即消失。
⸻
例句 3:递归太深了,Stack Overflow(栈溢出)
“这递归 5000 层了,stack 顶不住。”
场景: 深度递归每进入一层,就压一层 stack frame,最后爆掉。
你肯定听过 Stack Overflow(程序错误,不是那个网站),就是字面含义。
⸻
例句 4:这个函数调用链太长,stack 占用很大
“这个算法层级太深,stack frame 撑满了,得改循环写法。”
场景: 函数 A 调 B 调 C 调 D…… 每一层都在 stack 上放一帧。
⸻
例句 1:别忘了 free,不然 heap 会泄漏(内存泄漏)
“你 malloc 了不 free,这程序跑一小时 heap 都被你泄得差不多了。”
场景: 常见 bug:申请 heap 空间但忘了释放。
heap 内存泄漏就像你冰箱里丢垃圾,不清会越来越多。
⸻
例句 2:这个对象很大,放 stack 不行,放 heap
“这个 10MB 的结构必须 new 分配,stack 放不下。”
场景: 大对象必须放 heap,stack 会爆。
⸻
例句 3:GC(垃圾回收)在清 heap
“程序卡一下,是在做 heap 的垃圾回收。”
场景: Java / Go / Python 某次停顿: 垃圾回收器正在整理 heap。
你能想象成:程序暂停一下清冰箱。
⸻
例句 4:Heap 碎片太严重了,得 compact 一下
“堆碎片太多了,GC compact 一下才能挤出连续空间。”
场景: heap 会碎掉变成不连续的小块,导致申请大对象失败。
stack 永远不会碎片化,heap 会。
⸻
例句 5:这个对象生命周期不确定,用 heap 合适
“这个状态要跨好几个函数,用 heap 存吧。”
场景: 程序里长期存在的对象,比如用户会话、游戏角色。
⸻
例句 1:stack 上放指针,heap 才放真实数据
“别搞混了,你在 stack 上放的是指向 heap 的指针,不是对象本身。”
场景: 常见误区: 以为 new 出来的对象在 stack 上,实际上: • stack:存指针 • heap:存对象
⸻
例句 2:stack frame 分配很快,heap 分配要走 allocator
“这个函数很快,因为全是 stack 分配,没有 heap 分配。”
场景: 性能优化时常说:stack 分配快如闪电,heap 慢得多。
⸻
例句 3:为了性能,减少 heap 分配,多用 stack
“GC 太慢了,把短期对象都放到 stack 上更快。”
场景: 高性能服务器里,程序员会尽量减少 heap 分配。
⸻
例句 4:不要从 stack 返回地址,不然指向无效内存
“你返回了一个 stack 地址,出了函数这个东西已经没了。”
场景: C/C++ 新手经典错误。
⸻
例句 5:这个对象生命周期不匹配,应该从 stack 改到 heap
“这个对象要跨函数用,别放 stack。”
场景: 设计对象生命周期时的常见讨论。
⸻
场景:处理用户请求(一个 Web 服务器)
1)请求进来 程序开一个线程 / 协程 → 新建 stack 像料理台一样准备好临时空间
2)解析用户输入 临时变量、局部字符串 → 放 stack
3)要查询数据库,结果要返回给前端 数据结构 → 放 heap(因为需要长期存在一会儿)
4)stack 上创建指向 heap 数据的指针 就像料理台放一张便签写“冰箱有鸡肉(heap 地址)”
5)请求结束 stack 自动消失 heap 中的对象如果不再需要 → GC 或 free
⸻
python
def handle_message(node_id, raw_payload):
# ------------- STACK(函数临时工作区)-------------
# 这些变量存在于函数调用期间,用完就被丢弃
parsed = raw_payload.decode(”utf-8“) # 临时解析结果 → stack
print(f”收到节点 {node_id} 的消息:{parsed}“)
# 创建一个临时 dict(仍在 stack 上,短期变量)
result = {
”node“: node_id,
”text“: parsed,
}
# 调用另一个函数,把 result 传下去
store_message(result) # result 会作为“值”传递,但引用活在 heap(见下)
# ---------------------------------------------------
def store_message(msg):
# ------------- STACK(此函数自己的临时空间)-------------
# msg 是一个引用变量,放在 stack
# 但 msg 真实内容(dict)在 heap
print(”正在保存消息...“)
# 这个 append 会把 msg(字典对象)放入 heap 里的全局列表
MESSAGE_DB.append(msg) # 这里的 MESSAGE_DB 和里面的对象在 heap
# ---------------------------------------------------
# ------------- HEAP(进程长期存放数据的区域)-------------
# 程序运行期间一直存在,只要你不关程序,这里就不会消失
MESSAGE_DB = [] # 全局列表 → heap,存活时间 = 程序整个生命周期
# ----------------------------------------------------------
# 模拟收到一条 LoRa / Meshtastic 数据
handle_message(”node_01“, b”Hello Mesh“)
handle_message(”node_02“, b”GPS: 31.2304,121.4737“)
print(”\n所有存档的消息:“)
for m in MESSAGE_DB:
print(m)
运行结果:
收到节点 node_01 的消息:Hello Mesh
正在保存消息...
收到节点 node_02 的消息:GPS: 31.2304,121.4737
正在保存消息...
所有存档的消息:
{’node‘: ’node_01‘, ’text‘: ’Hello Mesh‘}
{’node‘: ’node_02‘, ’text‘: ’GPS: 31.2304,121.4737‘}
目前不一定要全部都看懂,但是可以理解一下意思。