You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Elixir/Erlang函数调用传参的变量内存管理机制是怎样的

Elixir/Erlang 函数调用的变量内存管理与传参机制

Elixir 编译后完全运行在 BEAM 虚拟机上,和 Erlang 共享完全一致的内存管理与传参逻辑,不存在语言层面的实现差异。

基础内存规则

  • BEAM 中所有运行时值统称为 term,所有 term 天生不可变,创建后不存在任何原地修改的可能。
  • 每个进程持有完全独立的私有栈、私有堆,不同进程默认不能直接访问对方内存空间里的内容。
  • term 按存储方式分为三类:
    • 即时值:小整数、原子、nil、本地pid这类体积不超过一个机器字长的值,直接存在寄存器、栈槽里,不需要额外分配堆内存
    • 复合值:列表、元组、普通映射、小二进制(长度小于64字节)这类结构,存储在所属进程的私有堆上
    • 大二进制:长度大于等于64字节的二进制,统一存在全局共享堆上,靠引用计数管理生命周期

同进程函数调用的传参逻辑

同进程内的函数调用是BEAM里最高频的操作,传参全程不会做任何深拷贝:

  • 即时值直接复制本身的值,传入被调用函数的参数寄存器或对应栈帧位置,开销是固定常数级
  • 存在私有堆上的复合值,传参时仅复制指向对应堆内存的引用(本质是指针),不会递归拷贝整个结构,哪怕是百万级长度的列表、GB级的元组,传参开销也是O(1)
  • 全局堆上的大二进制,传参时仅复制引用,同时给对应二进制的引用计数+1

这种实现不会产生传引用常见的副作用问题:因为所有term不可变,被调用函数拿到引用后,根本无法修改原term的内容,任何对参数的“修改”操作(比如给列表追加元素、更新映射键值)都会在堆上创建新的term,完全不会影响调用方持有的原始值。从开发者的语义感知上,这种行为和纯传值完全一致。

跨进程传参的特殊逻辑

如果参数是通过消息发送传递给其他进程(跨进程的函数调用本质是消息交互),规则会有区别:

  • 即时值直接复制值传递,和同进程逻辑一致
  • 私有堆上的复合值会做完整的深拷贝,复制到接收进程的私有堆上,拷贝完成后两个进程持有的是完全独立的两份值,互不影响
  • 大二进制仍然只传递引用,引用计数+1,不做拷贝,不可变特性从根源上避免了并发修改问题

传参类型的术语界定

BEAM的传参既不是教科书定义的纯传值,也不是纯传引用:

  • 从底层实现看,大值传参没有做全量拷贝,仅传递引用,不符合传统传值“传参时完整拷贝参数”的特征
  • 从语义表现看,不存在传引用特有的“函数内修改参数影响外部变量”的副作用,完全符合传值的行为预期
  • 行业内通常把BEAM的这种实现称为共享传参(call by sharing),是函数式语言依托不可变特性做的性能优化,在保证纯传值语义的前提下,避免了大值传参的拷贝开销。

内存回收逻辑

  • 进程私有堆上的term,当栈和寄存器中没有任何指向它的引用时,会在该进程下次触发GC时被回收。BEAM的GC是进程级独立运行的,不会阻塞其他进程执行。
  • 全局堆上的大二进制靠引用计数回收,当所有进程持有的对应引用都被释放时,内存会被直接回收,不需要等待GC扫描。

内容的提问来源于stack exchange,提问作者Aadarsh Bishen

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.26 10:18:22