作者:Lin Clark
译者:Cody Chan
原帖链接:A crash course in memory management
这是图解 SharedArrayBuffers 系列的第一篇:
为了更好地理解 JavaScript 里的 ArrayBuffer 和 SharedArrayBuffer,首先应该了解点内存管理知识
你可以把一个机器的内存想成一个一个的盒子,我把它们当作办公室的邮箱或者幼儿园里放小孩东西的格子
如果你想把某个东西留给某个小孩,你就把东西放到格子里
每个盒子旁边都有一个编号,这就是内存地址,用于告诉别人如何去找到你留的东西
每个盒子大小都一样,可以存放特定容量东西。盒子的具体容量取决于机器,这个大小我们称为字长,一般为 32 位或者 64 位。不过,为了展示方便,我这里字长用 8 位
如果你想把数字 2 放到盒子里,这很容易做到,因为数字很容易用二进制表示
但是如果你想放一个非数字类型的呢?比如字母 H?
我们需要一种方式可以用数字来表示它,这需要借助编码技术,类似 UTF-8 。首先,我们需要一个东西可以把字母转为数字……类似一个编码环,接下来就可以存储了
当我们从盒子里取回的时候,我们需要把取出的数字放到解码环里翻译回字母 H
自动内存管理
如果你是写 JavaScript 的,实际上你根本不需要关心内存,它对用户是透明的,这意味着你无法直接操纵内存
与此相对的, JS 引擎扮演着中间人角色,它为你管理好内存
我们用 React 举个例子,比如创建一个变量
JS 引擎通过一个编码器得到这个变量的二进制表示
然后,它会找到内存中可以放这个值的位置,这个过程称为内存分配
紧接着,引擎会持续追踪这个变量,看其是否还在程序中被使用。如果这个变量已经没有引用了,这块内存会被回收,JS 引擎就又可以在这里放新的值了
追踪内存中的变量(字符串、对象以及其它类型)并且在没有引用的时候清除它们的过程我们称作垃圾回收
类似 JavaScript 这种代码无法直接处理内存的语言叫做自动内存管理(memory-managed)语言
内存自动管理确实让开发者省了不少心,但是这也会带来额外的开销,这些额外开销会让程序的性能无法评估
手动内存管理
手动内存管理的语言不太一样,继续上面例子,看 React 用 C 写的话(借助 WebAssembly 是 可能的)会如何处理内存
不像 JavaScript,C 没有单独的内存管理抽象层,相反,你得直接与内存打交道。你可以从内存中直接拿东西,也可以直接往内存里存东西
当你把 C 或者其它语言编译为 WebAssembly 时,编译工具会在 WebAssembly 里增加一些辅助代码。比如,它会加入编解码的代码,这些代码我们称为运行时环境,运行时环境会帮助处理一些类似 JS 引擎帮 JS 做的事
但是,对于手动管理内存的语言,运行时不会包括垃圾回收
这并不意味着你需要完全由自己处理内存,即使在这类语言里,运行时你依然会得到一些辅助。比如,C 语言运行时会在一个 free list 里追踪可以使用的内存地址
你可以使用 malloc 函数(内存分配的简称)向运行时发出可以满足你数据存储需要的内存请求,这些内存会被从 free list 拿下来。当你用完了,需要调用 free 去释放内存,这些内存会被加回 free list
你必须清楚知道什么时候去调用这些函数,这就是为什么我们叫手动内存管理
作为一个开发者,知道什么时候去释放什么地方的内存是一件困难的事。如果你在错误时机处理了,这很可能会导致 BUG,甚至安全漏洞;如果你不处理,就会导致内存泄露
这就是为什么很多语言选择自动内存管理去避免人为错误,但是这牺牲了性能,下篇文章我还会谈到这些