Open Jocs opened 7 years ago
原文地址 由于个人能力知识有限,翻译过程中难免有纰漏和错误,还望指正
原文地址
由于个人能力知识有限,翻译过程中难免有纰漏和错误,还望指正
这是本系列文章中的第一篇:
在弄懂 ArrayBuffer 和 SharedArrayBuffer 为什么添加到 JavaScript 之前,你首先需要了解一些关于内存管理的知识。你可以把机器中的内存比喻成一组箱子,就像我把内存想象成办公室内部的信箱一样,或者是为学龄前儿童准备的用于存储杂物的小房间,如果你想给某位孩子准备一些礼物,你可以将物品放到某个箱子里。
在每个箱子旁边都有一个与之对应的数字,这就是内存地址。正是因为有了地址,你才能够告诉别人你为其准备动物品存放的位置。每个箱子具有相同的尺寸,也因此每个内存箱子也具有相同的容量来存储信息。箱子的尺寸是根据不同的机器而定的。箱子的尺寸被称作「字长」。它通常被标识为「32位」或者「64位」。但是为了简单的展示,在本文中我们使用「8位」的字长。(译者注:一个字长包含 8 个二进制位,也就是说一个内存单元的容量是 8 位)
如果你打算将数字 2 放进其中一个内存箱子里,这将非常容易做到,因为数字可以很容易通过二进制来表示。
倘若我们想放入内存箱子中的不是数字,而比如是字母「H」,怎么办呢?我们需要通过某种方法将其转化成可以使用数字来表示。为了完成此项工作,我们需要编码。类似于 UTF-8 。同时我们需要某种工具来按照 UTF-8 中的对应关系将字符转化成数字...比如说一个编码环。有了编码和编码环后,我们就可以将任意字符存入到内存箱子中了。
当我们打算将我们存入内存箱子中的信息取出时,我们需要将其放入一个解码器中,通过解码器将存放的数字转换成字母「H」。当你使用 JavaScript 工作时,你无须关心内存是怎样分配和使用的,因为在 JavaScript 中内存是自动管理的,内存管理和你的代码完全隔离。这意味着你不能够直接操作内存。JS 引擎将作为中介的角色,帮我们管理内存。
让我们一些 JS 代码,比如在 React 中,我们需要创建一个变量并对其赋值。
JS 引擎的工作就是通过编码器将变量名转换成二进制表示。
然后在内存中找到闲余的空间用来存放上面转换后的二进制表示。这一过程被称作分配内存。
接下来,JS 引擎会跟踪该变量并判断在程序中该变量是否还能够获取到。如果该变量不能够再被获取到,那么该内存箱子将会被回收再利用,以便 JS 引擎能够分配新的值到该内存中。
JS 引擎监听变量所代表的字符串、对象、以及内存中的其他数据类型的数据,当这些值不能再被获取到的时候,JS 引擎将会把它们清除出内存,这一过程被称作「垃圾回收」。比如 JavaScript 语言,代码不能够直接操纵内存,被称作自动内存管理语言。这一自动内存管理机制会使得开发变得相对简单。但是自动内存管理也会带来一些头疼的地方。比如自动内存管理可能会带来性能不可预测。而手动进行内存管理的语言就不会有这些问题。比如,通过 C 语言内存管理的方式来写 React 代码(当然,通过WebAssembly 已经使得其成为现实)。C 语言没有 JavaScript 自动内存管理的这一层功能抽象。所以,你可以直接操作内存,你可以从内存中对去数据,你也可以操作内存将数据存入内存中。
当你讲其它语言比如 C 语言传递给 WebAssembly,你使用的工具将会添加一些代码到 WebAssembly 中,比如,将添加对字节进行编码和解码的代码。这些代码被称作运行时环境。运行时环境也将像 JS 引擎在 JavaScript 语言中的作用一样,处理一些与之相同的工作。
但是对于手动内存管理的语言来说,运行时环境并不包含垃圾回收。这就意味着你必须手动来进行垃圾回收,即使是手动内存管理的语言,你通常也可以从该语言运行时环境中获取一些帮助。比如,在 C 语言中,C 语言运行时将会跟踪那些未被使用的内存,并将内存地址存储在一个链表中,该列表被称作「free list」。
你可以使用 malloc 函数(memory allocate 简写)来请求运行时环境来寻找能够存放你数据的内存地址。这会使得这些内存地址从「free list」中移除。当你使用数据完成工作后,你必须通过free函数来讲该内存释放。这样该内存地址将会被重新添加至「free list」中。你必须知道什么时候该调用这些函数。这也是为什么称为手动内存管理的原因所在 -- 你完全自己管理程序中的内存。作为开发者,断定什么时候该清除内存是一件相当困难的事。如果在错误的时间点清除内存,将导致程序 bug,甚至一些安全漏洞。如果不对不在使用的内存进行处理,又将导致内存用尽。这也就是为什么现代语言都是用自动内存管理的原因 -- 避免人为错误。但是这也将会产生一些性能上的问题。我将在下一篇对此进行说明。
malloc
free
get it
这是本系列文章中的第一篇:
在弄懂 ArrayBuffer 和 SharedArrayBuffer 为什么添加到 JavaScript 之前,你首先需要了解一些关于内存管理的知识。你可以把机器中的内存比喻成一组箱子,就像我把内存想象成办公室内部的信箱一样,或者是为学龄前儿童准备的用于存储杂物的小房间,如果你想给某位孩子准备一些礼物,你可以将物品放到某个箱子里。
在每个箱子旁边都有一个与之对应的数字,这就是内存地址。正是因为有了地址,你才能够告诉别人你为其准备动物品存放的位置。每个箱子具有相同的尺寸,也因此每个内存箱子也具有相同的容量来存储信息。箱子的尺寸是根据不同的机器而定的。箱子的尺寸被称作「字长」。它通常被标识为「32位」或者「64位」。但是为了简单的展示,在本文中我们使用「8位」的字长。(译者注:一个字长包含 8 个二进制位,也就是说一个内存单元的容量是 8 位)
如果你打算将数字 2 放进其中一个内存箱子里,这将非常容易做到,因为数字可以很容易通过二进制来表示。
倘若我们想放入内存箱子中的不是数字,而比如是字母「H」,怎么办呢?我们需要通过某种方法将其转化成可以使用数字来表示。为了完成此项工作,我们需要编码。类似于 UTF-8 。同时我们需要某种工具来按照 UTF-8 中的对应关系将字符转化成数字...比如说一个编码环。有了编码和编码环后,我们就可以将任意字符存入到内存箱子中了。
当我们打算将我们存入内存箱子中的信息取出时,我们需要将其放入一个解码器中,通过解码器将存放的数字转换成字母「H」。当你使用 JavaScript 工作时,你无须关心内存是怎样分配和使用的,因为在 JavaScript 中内存是自动管理的,内存管理和你的代码完全隔离。这意味着你不能够直接操作内存。JS 引擎将作为中介的角色,帮我们管理内存。
让我们一些 JS 代码,比如在 React 中,我们需要创建一个变量并对其赋值。
JS 引擎的工作就是通过编码器将变量名转换成二进制表示。
然后在内存中找到闲余的空间用来存放上面转换后的二进制表示。这一过程被称作分配内存。
接下来,JS 引擎会跟踪该变量并判断在程序中该变量是否还能够获取到。如果该变量不能够再被获取到,那么该内存箱子将会被回收再利用,以便 JS 引擎能够分配新的值到该内存中。
JS 引擎监听变量所代表的字符串、对象、以及内存中的其他数据类型的数据,当这些值不能再被获取到的时候,JS 引擎将会把它们清除出内存,这一过程被称作「垃圾回收」。比如 JavaScript 语言,代码不能够直接操纵内存,被称作自动内存管理语言。这一自动内存管理机制会使得开发变得相对简单。但是自动内存管理也会带来一些头疼的地方。比如自动内存管理可能会带来性能不可预测。而手动进行内存管理的语言就不会有这些问题。比如,通过 C 语言内存管理的方式来写 React 代码(当然,通过WebAssembly 已经使得其成为现实)。C 语言没有 JavaScript 自动内存管理的这一层功能抽象。所以,你可以直接操作内存,你可以从内存中对去数据,你也可以操作内存将数据存入内存中。
当你讲其它语言比如 C 语言传递给 WebAssembly,你使用的工具将会添加一些代码到 WebAssembly 中,比如,将添加对字节进行编码和解码的代码。这些代码被称作运行时环境。运行时环境也将像 JS 引擎在 JavaScript 语言中的作用一样,处理一些与之相同的工作。
但是对于手动内存管理的语言来说,运行时环境并不包含垃圾回收。这就意味着你必须手动来进行垃圾回收,即使是手动内存管理的语言,你通常也可以从该语言运行时环境中获取一些帮助。比如,在 C 语言中,C 语言运行时将会跟踪那些未被使用的内存,并将内存地址存储在一个链表中,该列表被称作「free list」。
你可以使用
malloc
函数(memory allocate 简写)来请求运行时环境来寻找能够存放你数据的内存地址。这会使得这些内存地址从「free list」中移除。当你使用数据完成工作后,你必须通过free
函数来讲该内存释放。这样该内存地址将会被重新添加至「free list」中。你必须知道什么时候该调用这些函数。这也是为什么称为手动内存管理的原因所在 -- 你完全自己管理程序中的内存。作为开发者,断定什么时候该清除内存是一件相当困难的事。如果在错误的时间点清除内存,将导致程序 bug,甚至一些安全漏洞。如果不对不在使用的内存进行处理,又将导致内存用尽。这也就是为什么现代语言都是用自动内存管理的原因 -- 避免人为错误。但是这也将会产生一些性能上的问题。我将在下一篇对此进行说明。