摘要:本文作為第三篇,將會討論另一個開發(fā)者容易忽視的重要主題內(nèi)存管理。我們也會提供一些關(guān)于如何處理內(nèi)存泄露的技巧。這是當(dāng)前整型和雙精度的大小。然而,這是一組可以收集的內(nèi)存空間的近似值。
本文轉(zhuǎn)載自:眾成翻譯
譯者:Leslie Wang
審校: 為之漫筆
鏈接:http://www.zcfy.cc/article/4211
原文:https://blog.sessionstack.com/how-javascript-works-memory-management-how-to-handle-4-common-memory-leaks-3f28b94cfbec
幾周前,我們開始寫一個系列,深入探討JavaScript和它的工作原理。我們認為了解JavaScript的構(gòu)成以及它們?nèi)绾螀f(xié)作,有助于編寫出更好的代碼和應(yīng)用程序。
本系列第一篇重點介紹了引擎、運行時、調(diào)用棧。第二篇揭示了谷歌V8 JavaScript引擎的內(nèi)部機制,并且提供了一些關(guān)于如何寫出更好的JavaScript代碼的建議。
本文作為第三篇,將會討論另一個開發(fā)者容易忽視的重要主題 :內(nèi)存管理。我們也會提供一些關(guān)于如何處理JavaScript內(nèi)存泄露的技巧。在SessionStack,我們需要確保不會造成內(nèi)存泄露或者不會增加我們集成的Web應(yīng)用的內(nèi)存消耗。
概述某些語言,比如C有低級的原生內(nèi)存管理原語,像malloc()和free()。開發(fā)人員使用這些原語可以顯式分配和釋放操作系統(tǒng)的內(nèi)存。
相對地,JavaScript會在創(chuàng)建變量(對象、字符串)時自動分配內(nèi)存,并在這些變量不被使用時自動釋放內(nèi)存,這個過程被稱為垃圾回收。這個“自動”釋放資源的特性帶來了很多困惑,讓JavaScript(和其他高級級語言)開發(fā)者誤以為可以不關(guān)心內(nèi)存管理。這是一個很大的錯誤
即使使用高級級語言,開發(fā)者也應(yīng)該對于內(nèi)存管理有一定的理解(至少有基本的理解)。有時自動內(nèi)存管理存在一些問題(例如垃圾回收實現(xiàn)可能存在缺陷或者不足),開發(fā)者必須弄明白這些問題,以便找一個合適解決方法。
內(nèi)存生命周期無論你用哪一種編程語言,內(nèi)存生命周期幾乎總是一樣的:
Here is an overview of what happens at each step of the cycle:
這是對生命周期中的每一步大概的說明:
分配內(nèi)存—?內(nèi)存是被操作系統(tǒng)分配,這允許程序使用它。在低級語言中(例如C),這是一個作為開發(fā)者需要處理的顯式操作。在高級語言中,然而,這些操作都代替開發(fā)者進行了處理。
使用內(nèi)存。實際使用之前分配的內(nèi)存,通過在代碼操作變量對內(nèi)在進行讀和寫。
釋放內(nèi)存?。不用的時候,就可以釋放內(nèi)存,以便重新分配。與分配內(nèi)存操作一樣,釋放內(nèi)存在低級語言中也需要顯式操作。
想要快速的了解堆棧和內(nèi)存的概念,可以閱讀本系列第一篇文章。
什么是內(nèi)存在直接探討Javascript中的內(nèi)存之前,我們先簡要的討論一下什么是內(nèi)存、內(nèi)存大概是怎么樣工作的。
在硬件中,電腦的內(nèi)存包含了大量的觸發(fā)電路,每一個觸發(fā)電路都包含一些能夠儲存1位數(shù)據(jù)的晶體管。觸發(fā)器通過唯一標(biāo)識符來尋址,從而可以讀取和覆蓋它們。因此,從概念上來講,可以認為電腦內(nèi)存是一個巨大的可讀寫陣列。
人類不善于把我們所有的思想和算術(shù)用位運算來表示,我們把這些小東西組織成一個大家伙,這些大家伙可以用來表現(xiàn)數(shù)字:8位是一個字節(jié)。字節(jié)之上是字(16位、32位)。
許多東西被存儲在內(nèi)存中:
所有的變量和程序中用到的數(shù)據(jù);
程序的代碼,包括操作系統(tǒng)的代碼。
編譯器和操作系統(tǒng)共同工作幫助開發(fā)者完成大部分的內(nèi)存管理,但是我們推薦你了解一下底層到底發(fā)生了什么。
編譯代碼的時候,編譯器會解析原始數(shù)據(jù)類型,提前計算出它們需要多大的內(nèi)存空間。然后將所需的數(shù)量分配在棧空間中。之所以稱為棧空間,是因在函數(shù)被調(diào)用的時候,他們的內(nèi)存被添加在現(xiàn)有內(nèi)存之上(就是會在棧的最上面添加一個棧幀來指向存儲函數(shù)內(nèi)部變量的空間)。終止的時候,以LIFO(后進先出)的順序移除這些調(diào)用。例如:
int n; // 4字節(jié) int x[4]; // 4個元素的數(shù)組,每個元素4字節(jié) double m; // 8字節(jié)
編譯器馬上知道需要內(nèi)存
4 + 4 × 4 + 8 = 28字節(jié)。
這是當(dāng)前整型和雙精度的大小。大約20年以前,整型通常只需要2個字節(jié),雙精度需要4個字節(jié),你的代碼不受基礎(chǔ)數(shù)據(jù)類型大小的限制。
編譯器會插入與操作系統(tǒng)交互的代碼,來請求棧中必要大小的字節(jié)來儲存變量。
在上面的例子中,編輯器知道每個變量準(zhǔn)確的地址。事實上,無論什么時候我們寫變量n,將會在內(nèi)部被翻譯成類似“memory address 4127963”的語句。
注意,如果我們嘗試訪問x[4]的內(nèi)存(開始聲明的x[4]是長度為4的數(shù)組,x[4]表示第五個元素),我們會訪問m的數(shù)據(jù)。那是因為我們正在訪問一個數(shù)組里不存在的元素,m比數(shù)組中實際分配內(nèi)存的最后一個元素x[3]要遠4個字節(jié),可能最后的結(jié)果是讀取(或者覆蓋)了m的一些位。這肯定會對其他程序產(chǎn)生不希望產(chǎn)生的結(jié)果。
當(dāng)函數(shù)調(diào)用其他函數(shù)的時候,每一個函數(shù)被調(diào)用的時候都會獲得自己的棧塊。在自己的棧塊里會保存函數(shù)內(nèi)所有的變量,還有一個程序計數(shù)器會記錄變量執(zhí)行時所在的位置。當(dāng)函數(shù)執(zhí)行完之后,會釋放它的內(nèi)存以作他用。
動態(tài)分配不幸的是,事情并不是那么簡單,因為在編譯的時候我們并不知道一個變量將會需要多少內(nèi)存。假設(shè)我們做了下面這樣的事:
int n = readInput(); //讀取用戶的輸入 ... //創(chuàng)建一個有n個元素的數(shù)組
編譯器不知道這個數(shù)組需要多少內(nèi)存,因為數(shù)組大小取決于用戶提供的值。
因此,此時不能在棧上分配空間。程序必須在運行時向操作系統(tǒng)請求夠用的空間。此時內(nèi)存從堆空間中被分配。靜態(tài)與動態(tài)分配內(nèi)存之間的不同在下面的表格中被總結(jié)出來:
靜態(tài)分配內(nèi)存與動態(tài)分配內(nèi)存的區(qū)別。
為了完全理解動態(tài)內(nèi)存是如何分配的,我們需要花更多的時間在指針上,這個可能很大程度上偏離了這篇文章的主題。如果你有興趣學(xué)習(xí)更多的知識,那就在評論中讓我知道,我就可以在之后的文章中寫更多關(guān)于指針的細節(jié)。
JavaScript中的內(nèi)存分配現(xiàn)在我們來解釋JavaScript中的第一步(分配內(nèi)存)是如何工作的。
JavaScript在開發(fā)者聲明值的時候自動分配內(nèi)存。
var n = 374; // 為數(shù)值分配內(nèi)存 var s = "sessionstack"; //為字符串分配內(nèi)存 var o = { a: 1, b: null }; //為對象和它包含的值分配內(nèi)存 var a = [1, null, "str"]; //為數(shù)組和它包含的值分配內(nèi)存 function f(a) { return a + 3; } //為函數(shù)(可調(diào)用的對象)分配內(nèi)存 //函數(shù)表達式也會分配一個對象 someElement.addEventListener("click", function() { someElement.style.backgroundColor = "blue"; }, false); //一些函數(shù)調(diào)用也會導(dǎo)致對象分配 `var d = new Date(); // allocates a Date object` //分配一個Date對象的內(nèi)存 `var e = document.createElement("div"); //分配一個DOM元素的內(nèi)存 //方法可以分配新的值或者對象 var s1 = "sessionstack"; var s2 = s1.substr(0, 3); //s2是一個新的字符串 // 因為字符串是不可變的 // JavaScript可能決定不分配內(nèi)存 // 而僅僅存儲 0-3的范圍 var a1 = ["str1", "str2"]; var a2 = ["str3", "str4"]; var a3 = a1.concat(a2); //新的數(shù)組有4個元素是a1和a2連接起來的。在JavaScript中使用內(nèi)存
在JavaScript中使用被分配的內(nèi)存,本質(zhì)上就是對內(nèi)在的讀和寫。
比如,讀、寫變量的值或者對象的屬性,抑或向一個函數(shù)傳遞參數(shù)。
內(nèi)存不在被需要時釋放內(nèi)存大部分的內(nèi)存管理問題都在這個階段出現(xiàn)。
這里最難的任務(wù)是找出這些被分配的內(nèi)存什么時候不再被需要。這常常要求開發(fā)者去決定程序中的一段內(nèi)存不在被需要而且釋放它。
高級語言嵌入了一個叫垃圾回收的軟件,它的工作是跟蹤內(nèi)存的分配和使用,以便于發(fā)現(xiàn)一些內(nèi)存在一些情況下不再被需要,它將會自動地釋放這些內(nèi)存。
不幸的是,這個過程是一個近似的過程,因為一般關(guān)于知道內(nèi)存是否是被需要的問題是不可判斷的(不能用一個算法解決)。
大部分的垃圾回收器會收集不再被訪問的內(nèi)存,例如指向它的所有變量都在作用域之外。然而,這是一組可以收集的內(nèi)存空間的近似值。因為在任何時候,一個內(nèi)存地址可能還有一個在作用域里的變量指向它,但是它將不會被再次訪問。
垃圾收集由于找到一些內(nèi)存是否是“不再被需要的”這個事實是不可判定的,垃圾回收的實現(xiàn)存在局限性。本節(jié)解釋必要的概念去理解主要的垃圾回收算法和它們的局限性。
內(nèi)存引用垃圾回收算法依賴的主要概念是引用。
在內(nèi)存管理的語境下,一個對象只要顯式或隱式訪問另一個對象,就可以說它引用了另一個對象。例如,JavaScript對象引用其Prototype(隱式引用),或者引用prototype對象的屬性值(顯式引用)。
在這種情況下,“對象”的概念擴展到比普通JavaScript對象更廣的范圍,并且還包含函數(shù)作用域。(或者global詞法作用域)
引用計數(shù)垃圾回收詞法作用域定義變量的名字在嵌套的函數(shù)中如何被解析:內(nèi)部的函數(shù)包含了父級函數(shù)的作用域,即使父級函數(shù)已經(jīng)返回。
這是最簡單的垃圾回收算法。 一個對象在沒有其他的引用指向它的時候就被認為“可被回收的”。
看一下下面的代碼:
var o1 = { o2: { x: 1 } }; //2個對象被創(chuàng)建 /"o2"被"o1"作為屬性引用 //誰也不能被回收 var o3 = o1; //"o3"是第二個引用"o1"指向?qū)ο蟮淖兞? o1 = 1; //現(xiàn)在,"o1"只有一個引用了,就是"o3" var o4 = o3.o2; // 引用"o3"對象的"o2"屬性 //"o2"對象這時有2個引用: 一個是作為對象的屬性 //另一個是"o4" o3 = "374"; //"o1"原來的對象現(xiàn)在有0個對它的引用 //"o1"可以被垃圾回收了。 //然而它的"o2"屬性依然被"o4"變量引用,所以"o2"不能被釋放。 o4 = null; //最初"o1"中的"o2"屬性沒有被其他的引用了 //"o2"可以被垃圾回收了循環(huán)引用創(chuàng)造麻煩
在涉及循環(huán)引用的時候有一個限制。在下面的例子中,兩個對象被創(chuàng)建了,而且相互引用,這樣創(chuàng)建了一個循環(huán)引用。它們會在函數(shù)調(diào)用后超出作用域,應(yīng)該可以釋放。然而引用計數(shù)算法考慮到2個對象中的每一個至少被引用了一次,因此都不可以被回收。
function f() { var o1 = {}; var o2 = {}; o1.p = o2; // o1 引用 o2 o2.p = o1; // o2 引用 o1. 形成循環(huán)引用 } f();標(biāo)記清除算法
為了決定一個對象是否被需要,這個算法用于確定是否可以找到某個對象。
這個算法包含以下步驟。
垃圾回收器生成一個根列表。根通常是將引用保存在代碼中的全局變量。在JavaScript中,window對象是一個可以作為根的全局變量。
所有的根都被檢查和標(biāo)記成活躍的(不是垃圾),所有的子變量也被遞歸檢查。所有可能從根元素到達的都不被認為是垃圾。
所有沒有被標(biāo)記成活躍的內(nèi)存都被認為是垃圾。垃圾回收器就可以釋放內(nèi)存并且把內(nèi)存還給操作系統(tǒng)。
上圖就是標(biāo)記清除示意。
這個算法就比之前的(引用計算)要好些,因為“一個對象沒有被引用”導(dǎo)致這個對象不能被訪問。相反,正如我們在循環(huán)引用的示例中看到的,對象不能被訪問到,不一定不存在引用。
2012年起,所有瀏覽器都內(nèi)置了標(biāo)記清除垃圾回收器。在過去幾年中,JavaScript垃圾回收領(lǐng)域中的所有改進(代/增量/并行/并行垃圾收集)都是由這個算法(標(biāo)記清除法)改進實現(xiàn)的,但并不是對垃圾收集算法本身的改進,也沒有改變它確定對象是否可達這個目標(biāo)。
推薦一篇文章,其中有關(guān)于跟蹤垃圾回收的細節(jié),包括了標(biāo)記清除法和它的優(yōu)化算法。
循環(huán)引用不再是問題在上面的例子中(循環(huán)引用的那個),在函數(shù)執(zhí)行完之后,這個2個對象沒有被任何可以到達的全局對象所引用。因此,他們將會被垃圾回收器發(fā)現(xiàn)為不可到達的。
盡管在這兩個對象之間有相互引用,但是他們不能從全局對象上到達。
垃圾回收器的反常行為盡管垃圾回收器很方便,但是他們有一套自己的方案。其中之一就是不確定性。換句話說,GC是不可預(yù)測的。你不可能知道一個回收器什么時候會被執(zhí)行。這意味著程序在某些情況下會使用比實際需求還要多的內(nèi)存。在其他情況下,在特別敏感的應(yīng)用程序中,可能會出現(xiàn)短停頓。盡管不確定意味著不能確定回收工作何時執(zhí)行,但大多數(shù)GC實現(xiàn)都會在分配內(nèi)存的期間啟動收集例程。如果沒有內(nèi)存分配,大部分垃圾回收就保持空閑。參考下面的情況。
執(zhí)行相當(dāng)大的一組分配。
這些元素中的大部分(或者所有的)都被標(biāo)記為不可到達的(假設(shè)我們清空了一個指向我們不再需要的緩存的引用。)
沒有更多的分配被執(zhí)行。
在這種情況下,大多數(shù)垃圾回收實現(xiàn)都不會做進一步的回收。換句話說,盡管這里有不可達的引用變量可供回收,回收器也不會管。嚴(yán)格講,這不是泄露,但結(jié)果卻會占用比通常情況下更多的內(nèi)存。
什么是內(nèi)存泄漏內(nèi)存泄漏基本上就是不再被應(yīng)用需要的內(nèi)存,由于某種原因,沒有被歸還給操作系統(tǒng)或者進入可用內(nèi)存池。
編程語言喜歡不同的管理內(nèi)存方式。然而,一段確定的內(nèi)存是否被使用是一個不可判斷的問題。換句話說,只有開發(fā)者才能弄清楚,是否一段內(nèi)存可以被還給操作系統(tǒng)。
某些編程語言為開發(fā)者提供了釋放內(nèi)存功能。另一些則期待開發(fā)者清楚的知道一段內(nèi)存什么時候是沒用的。Wikipedia有一篇非常好的關(guān)于內(nèi)存管理的文章。
4種常見的JavaScript內(nèi)存泄漏 1:全局變量JavaScript用一個有趣的方式管理未被聲明的變量:對未聲明的變量的引用在全局對象里創(chuàng)建一個新的變量。在瀏覽器的情況下,這個全局對象是window。換句話說:
function foo(arg) { bar = "some text"; }
等同于
function foo(arg) { window.bar = "some text"; }
如果bar被假定只在foo函數(shù)的作用域里引用變量,但是你忘記了使用var去聲明它,一個意外的全局變量就被聲明了。
在這個例子里,泄漏一個簡單的字符串不會造成很大的傷害,但是它確實有可能變得更糟。
另外一個意外創(chuàng)建全局變量的方法是通過this:
function foo() { this.var1 = "potential accidental global"; } // Foo作為函數(shù)調(diào)用,this指向全局變量(window) // 而不是undefined foo();
為了防止這些問題發(fā)生,可以在你的JaveScript文件開頭使用"use strict";。這個可以使用一種嚴(yán)格的模式解析JavaScript來阻止意外的全局變量。
除了意外創(chuàng)建的全局變量,明確創(chuàng)建的全局變量同樣也很多。這些當(dāng)然屬于不能被回收的(除非被指定為null或者重新分配)。特別那些用于暫時存儲數(shù)據(jù)的全局變量,是非常重要的。如果你必須要使用全局變量來存儲大量數(shù)據(jù),確保在是使用完成之后為其賦值null或者重新賦其他值。
2: 被遺忘的定時器或者回調(diào)在JavaScript中使用setInterval是十分常見的。
大多數(shù)庫,特別是提供觀察器或其他接收回調(diào)的實用函數(shù)的,都會在自己的實例無法訪問前把這些回調(diào)也設(shè)置為無法訪問。但涉及setInterval時,下面這樣的代碼十分常見:
var serverData = loadData(); setInterval(function() { var renderer = document.getElementById("renderer"); if(renderer) { renderer.innerHTML = JSON.stringify(serverData); } }, 5000); //每5秒執(zhí)行一次
定時器可能會導(dǎo)致對不需要的節(jié)點或者數(shù)據(jù)的引用。
renderer對象在將來有可能被移除,讓interval處理器內(nèi)部的整個塊都變得沒有用。但由于interval仍然起作用,處理程序并不能被回收(除非interval停止)。如果interval不能被回收,它的依賴也不可能被回收。這就意味著serverData,大概保存了大量的數(shù)據(jù),也不可能被回收。
在觀察者的情況下,在他們不再被需要(或相關(guān)對象需要設(shè)置成不能到達)的時候明確的調(diào)用移除是非常重要的。
在過去,這一點尤其重要,因為某些瀏覽器(舊的IE6)不能很好的管理循環(huán)引用(更多信息見下文)。如今,大部分的瀏覽器都能而且會在對象變得不可到達的時候回收觀察處理器,即使監(jiān)聽器沒有被明確的移除掉。然而,在對象被處理之前,要顯式地刪除這些觀察者仍然是值得提倡的做法。例如:
var element = document.getElementById("launch-button"); var counter = 0; function onClick(event) { counter++; element.innerHtml = "text " + counter; } element.addEventListener("click", onClick); // 做點事 element.removeEventListener("click", onClick); element.parentNode.removeChild(element); // 當(dāng)元素被銷毀 //元素和事件都會即使在老的瀏覽器里也會被回收
如今的瀏覽器(包括IE和Edge)使用現(xiàn)代的垃圾回收算法,可以立即發(fā)現(xiàn)并處理這些循環(huán)引用。換句話說,先調(diào)用removeEventListener再刪節(jié)點并非嚴(yán)格必要。
jQuery等框架和插件會在丟棄節(jié)點前刪除監(jiān)聽器。這都是它們內(nèi)部處理,以保證不會產(chǎn)生內(nèi)存泄漏,甚至是在有問題的瀏覽器(沒錯,IE6)上也不會。
3: 閉包閉包是JavaScript開發(fā)的一個關(guān)鍵方面:一個內(nèi)部函數(shù)使用了外部(封閉)函數(shù)的變量。由于JavaScript運行時實現(xiàn)的不同,它可能以下面的方式造成內(nèi)存泄漏:
var theThing = null; var replaceThing = function () { var originalThing = theThing; var unused = function () { if (originalThing) // 引用"originalThing" console.log("hi"); }; theThing = { longStr: new Array(1000000).join("*"), someMethod: function () { console.log("message"); } }; }; setInterval(replaceThing, 1000);
這段代碼做了一件事:每次ReplaceThing被調(diào)用,theThing獲得一個包含大數(shù)組和新的閉包(someMethod)的對象。同時,變量unused保持了一個引用originalThing(theThing是上次調(diào)用replaceThing生成的值)的閉包。已經(jīng)有點困惑了吧?最重要的事情是一旦為同一父域中的作用域產(chǎn)生閉包,則該作用域是共享的。
這里,作用域產(chǎn)生了閉包,someMethod和unused共享這個閉包中的內(nèi)存。unused引用了originalThing。盡管unused不會被使用,someMethod可以通過theThing來使用replaceThing作用域外的變量(例如某些全局的)。而且someMethod和unused有共同的閉包作用域,unused對originalThing的引用強制oriiginalThing保持激活狀態(tài)(兩個閉包共享整個作用域)。這阻止了它的回收。
當(dāng)這段代碼重復(fù)執(zhí)行,可以觀察到被使用的內(nèi)存在持續(xù)增加。垃圾回收運行的時候也不會變小。從本質(zhì)上來說,閉包的連接列表已經(jīng)創(chuàng)建了(以theThing變量為根),這些閉包每個作用域都間接引用了大數(shù)組,導(dǎo)致大量的內(nèi)存泄漏。
這個問題被Meteor團隊發(fā)現(xiàn),他們有一篇非常好的文章描述了閉包大量的細節(jié)。
4: DOM外引用有的時候在數(shù)據(jù)結(jié)構(gòu)里存儲DOM節(jié)點是非常有用的,比如你想要快速更新一個表格幾行的內(nèi)容。此時存儲每一行的DOM節(jié)點的引用在一個字典或者數(shù)組里是有意義的。此時一個DOM節(jié)點有兩個引用:一個在dom樹中,另外一個在字典中。如果在未來的某個時候你想要去移除這些排,你需要確保兩個引用都不可到達。
var elements = { button: document.getElementById("button"), image: document.getElementById("image") }; function doStuff() { image.src = "http://example.com/image_name.png"; } function removeImage() { //image是body元素的子節(jié)點 document.body.removeChild(document.getElementById("image")); //這個時候我們在全局的elements對象里仍然有一個對#button的引用。 //換句話說,buttom元素仍然在內(nèi)存中而且不能被回收。 }
當(dāng)涉及DOM樹內(nèi)部或子節(jié)點時,需要考慮額外的考慮因素。例如,你在JavaScript中保持對某個表的特定單元格的引用。有一天你決定從DOM中移除表格但是保留了對單元格的引用。人們也許會認為除了單元格其他的都會被回收。實際并不是這樣的:單元格是表格的一個子節(jié)點,子節(jié)點保持了對父節(jié)點的引用。確切的說,JS代碼中對單元格的引用造成了整個表格被留在內(nèi)存中了,所以在移除有被引用的節(jié)點時候要當(dāng)心。
我們在SessionStack努力遵循這些最佳實踐,因為:
一旦你整合essionStack到你的生產(chǎn)應(yīng)用中,它就開始記錄所有的事情:DOM變化、用戶交互、JS異常、堆棧跟蹤、失敗的網(wǎng)絡(luò)請求、調(diào)試信息,等等。
通過SessionStack,你可以回放應(yīng)用中的問題,看到問題對用戶的影響。所有這些都不會對你的應(yīng)用產(chǎn)生性能的影響。因為用戶可以重新加載頁面或者在應(yīng)用中跳轉(zhuǎn),所有的觀察者、攔截器、變量分配都必須合理處置。以免造成內(nèi)存泄漏,也預(yù)防增加整個應(yīng)用的內(nèi)存占用。
這是一個免費的計劃,你現(xiàn)在可以嘗試一下。
歡迎關(guān)注我的公眾號,關(guān)注前端文章:
參考資料http://www-bcf.usc.edu/~dkempe/CS104/08-29.pdf
https://blog.meteor.com/an-interesting-kind-of-javascript-memory-leak-8b47d2e7f156
http://www.nodesimplified.com/2017/08/javascript-memory-management-and.html
Programming
JavaScript
Web Development
Tutorial
Memory Leak
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://specialneedsforspecialkids.com/yun/88780.html
摘要:是如何工作的內(nèi)存管理以及如何處理四種常見的內(nèi)存泄漏原文譯者幾個禮拜之前我們開始一系列對于以及其本質(zhì)工作原理的深入挖掘我們認為通過了解的構(gòu)建方式以及它們是如何共同合作的,你就能夠?qū)懗龈玫拇a以及應(yīng)用。 JavaScript是如何工作的:內(nèi)存管理以及如何處理四種常見的內(nèi)存泄漏 原文:How JavaScript works: memory management + how to han...
摘要:本系列的第一篇文章著重提供一個關(guān)于引擎運行時和調(diào)用棧的概述。在硬件層面,計算機內(nèi)存由大量的觸發(fā)器組成。每個觸發(fā)器包含幾個晶體管能夠存儲一個比特譯注位。可以通過唯一標(biāo)識符來訪問單個觸發(fā)器,所以可以對它們進行讀寫操作。比特稱為個字節(jié)。 原文 How JavaScript works: memory management + how to handle 4 common memory lea...
摘要:前端日報精選在中的元素種類及性能優(yōu)化譯異步遞歸回調(diào)譯定位一個頁面阻塞問題的排查過程前端分享之的使用及單點登錄中文視頻如何用做好一個大型應(yīng)用云際個實用技巧眾成翻譯年一定不要錯過的五本編程書籍年前端領(lǐng)域有哪些探索和實踐實現(xiàn)一個時光網(wǎng)掘金 2017-09-22 前端日報 精選 JavaScript 在 V8 中的元素種類及性能優(yōu)化【譯】異步遞歸:回調(diào)、Promise、Async[譯]HTML...
摘要:這是因為我們訪問了數(shù)組中不存在的數(shù)組元素它超過了最后一個實際分配到內(nèi)存的數(shù)組元素字節(jié),并且有可能會讀取或者覆寫的位。包含個元素的新數(shù)組由和數(shù)組元素所組成中的內(nèi)存使用中使用分配的內(nèi)存主要指的是內(nèi)存讀寫。 原文請查閱這里,本文有進行刪減,文后增了些經(jīng)驗總結(jié)。 本系列持續(xù)更新中,Github 地址請查閱這里。 這是 JavaScript 工作原理的第三章。 我們將會討論日常使用中另一個被開發(fā)...
閱讀 1750·2021-09-28 09:43
閱讀 1111·2021-09-23 11:22
閱讀 2707·2021-09-14 18:05
閱讀 1822·2019-08-30 15:52
閱讀 2811·2019-08-30 10:55
閱讀 2007·2019-08-29 16:58
閱讀 1323·2019-08-29 16:37
閱讀 3030·2019-08-29 16:25