JavaScript 中的垃圾收集 (GC)?
JavaScript 中的内存管理比 C 或 C++ 等低级语言容易得多。与低级语言不同,JavaScript 会自动检测哪些对象在以后的情况下会需要,哪些对象是无缘无故占用内存的垃圾。在本文中,我们将讨论 JavaScript 中的垃圾收集 (GC) 的工作原理。我们将详细讨论几个重要部分 -
从根开始的可达性
互连对象(不同对象之间的互连)
无法访问的集合(断开的链接)
可达性
内存管理的第一个概念是可达性。在这种模式下,垃圾收集器会尝试从某个点搜索可达值。当某个值或内存位置可从某个点访问时,则不会将其删除。对象可以直接或间接地从某个位置访问。有几种可能的方法可以确定对象是否可访问−
当函数当前正在执行时,其局部变量和参数是可直接访问的。
如果可以从当前执行的函数调用其他一些函数,也可以访问。不仅是该函数,链中这些函数的参数和局部变量也将是可访问的。
全局变量始终可访问
这些直接可访问的对象称为根对象。当从根可访问的其他一些值也被标记为可访问时。例如,假设我们有一个对象 ob1,作为我们的全局对象。由于 ob1 是全局的,因此它是根。现在 ob1 中有另一个引用,指向 ob2。第二个对象 ob2 不是直接可访问的,但可以从根对象 ob1 访问。因此,它也将被标记为可访问。当某个对象变得不可访问时,名为垃圾收集器的后台低优先级进程会从内存中清除这些不可访问的对象。
示例
使用代码和一些图表展示了上述想法。
代码
let company = { name: "Tutorialspoint" };
该图描绘了一个 company 对象,该对象存储了一个名为"Tutorialspoint"的字符串变量。现在可以通过名为"company"的引用访问此对象。此时,如果我们将 company 设置为 null,则引用将丢失。
代码
company = null;
将 company = null 后,该对象将无法访问。因此垃圾收集器将依次删除此部分。
让我们看另一个示例,其中我们使用两个引用。让我们考虑以下代码,其中 company 对象由另一个名为 ob 的变量引用。
代码
let company = { name: "Tutorialspoint" }; let ob = company;
Then, if we set company = null, the state will be like below−
因此,该对象由另一个名为"ob"的链接引用。换句话说,该对象可通过"ob"访问。这就是垃圾收集器不会将其从内存中删除的原因。
互连对象
Javascript 对象可以与多个对象链接在一起。或者我们可以说它们可以与一些类似的对象互连。在这种情况下,结构变得更加复杂。让我们看一个例子来了解互连对象。
示例
代码
function parent(dad, daughter) { dad.daughter = daughter; daughter.dad = dad; return { father: dad, child: daughter } } let parent = parent({ name: "Bob" }, { name: "Alice" });
此示例显示 parent() 函数正在创建两个对象,即父对象和子对象,名称分别为"Bob"和"Alice",这两个对象通过"爸爸"和"女儿"关系相互关联。在下图中,我们可以直观地看到这种情况。它表明,这两个对象是相互关联的。
此时,如果我们删除一些连接,例如−
代码
delete parent_child.father delete parent_child.child.dad
删除这两个链接后,父亲(Bob)没有传入链接。垃圾收集器将从内存中删除整个父亲对象。下图描述了这个想法。
无法到达的岛屿
第三种情况可能是无法到达的岛屿。考虑上一节(互连对象)中显示的相同示例。现在,如果我们从父对象中删除链接,它将分离整个对象,如下图所示 -
父链接被删除,因此整个对象和父对象以及子对象都无法访问。它们形成了一个无法到达的岛屿。
代码
parent = null;
结论
与任何其他语言一样,内存管理是一项非常重要的任务,最重要的是,清除未使用的内存块对于编写更好、更高效的代码是必要的。JavaScript 会自动收集无用内存块的信息并将其从内存中删除。垃圾收集器从根对象搜索可达性,以确定对象将来是否会被使用。对于互连对象,它会搜索对象是否没有传入边,如果无法到达该对象,则它会从内存中删除该对象。