摘要:本系列的第一篇文章簡(jiǎn)單介紹了引擎運(yùn)行時(shí)間和堆棧的調(diào)用。編譯器將插入與操作系統(tǒng)交互的代碼,并申請(qǐng)存儲(chǔ)變量所需的堆棧字節(jié)數(shù)。當(dāng)函數(shù)調(diào)用其他函數(shù)時(shí),每個(gè)函數(shù)在調(diào)用堆棧時(shí)獲得自己的塊。因此,它不能為堆棧上的變量分配空間。
本系列的第一篇文章簡(jiǎn)單介紹了引擎、運(yùn)行時(shí)間和堆棧的調(diào)用。第二篇文章研究了谷歌V8 JavaScript引擎的內(nèi)部機(jī)制,并介紹了一些編寫(xiě)JavaScript代碼的技巧。
在這第三篇文章中,我們將討論另一個(gè)重要主題——內(nèi)存管理,這是由于日常使用的編程語(yǔ)言越來(lái)越成熟和復(fù)雜,開(kāi)發(fā)人員容易忽視這一問(wèn)題。我們還將提供一些有關(guān)如何處理JavaScript中的內(nèi)存泄漏的技巧,在SessionStack中遵循這些技巧,既能確保SessionStack 不會(huì)導(dǎo)致內(nèi)存泄漏,也不會(huì)增加我們集成的Web應(yīng)用程序的內(nèi)存消耗。
想閱讀更多優(yōu)質(zhì)文章請(qǐng)猛戳GitHub博客,一年百來(lái)篇優(yōu)質(zhì)文章等著你!
概述像 C 這樣的編程語(yǔ)言,具有低級(jí)內(nèi)存管理原語(yǔ),如malloc()和free()。開(kāi)發(fā)人員使用這些原語(yǔ)顯式地對(duì)操作系統(tǒng)的內(nèi)存進(jìn)行分配和釋放。
而JavaScript在創(chuàng)建對(duì)象(對(duì)象、字符串等)時(shí)會(huì)為它們分配內(nèi)存,不再使用對(duì)時(shí)會(huì)“自動(dòng)”釋放內(nèi)存,這個(gè)過(guò)程稱(chēng)為垃圾收集。這種看“自動(dòng)”似釋放資源的的特性是造成混亂的根源,因?yàn)檫@給JavaScript(和其他高級(jí)語(yǔ)言)開(kāi)發(fā)人員帶來(lái)一種錯(cuò)覺(jué),以為他們可以不關(guān)心內(nèi)存管理的錯(cuò)誤印象,這是想法一個(gè)大錯(cuò)誤。
即使在使用高級(jí)語(yǔ)言時(shí),開(kāi)發(fā)人員也應(yīng)該了解內(nèi)存管理(或者至少懂得一些基礎(chǔ)知識(shí))。有時(shí)候,自動(dòng)內(nèi)存管理存在一些問(wèn)題(例如垃圾收集器中的bug或?qū)崿F(xiàn)限制等),開(kāi)發(fā)人員必須理解這些問(wèn)題,以便可以正確地處理它們(或者找到一個(gè)適當(dāng)?shù)慕鉀Q方案,以最小代價(jià)來(lái)維護(hù)代碼)。
內(nèi)存的生命周期無(wú)論使用哪種編程語(yǔ)言,內(nèi)存的生命周期都是一樣的:
這里簡(jiǎn)單介紹一下內(nèi)存生命周期中的每一個(gè)階段:
分配內(nèi)存 —? 內(nèi)存是由操作系統(tǒng)分配的,它允許您的程序使用它。在低級(jí)語(yǔ)言(例如C語(yǔ)言)中,這是一個(gè)開(kāi)發(fā)人員需要自己處理的顯式執(zhí)行的操作。然而,在高級(jí)語(yǔ)言中,系統(tǒng)會(huì)自動(dòng)為你分配內(nèi)在。
使用內(nèi)存 — 這是程序?qū)嶋H使用之前分配的內(nèi)存,在代碼中使用分配的變量時(shí),就會(huì)發(fā)生讀和寫(xiě)操作。
釋放內(nèi)存 — 釋放所有不再使用的內(nèi)存,使之成為自由內(nèi)存,并可以被重利用。與分配內(nèi)存操作一樣,這一操作在低級(jí)語(yǔ)言中也是需要顯式地執(zhí)行。
內(nèi)存是什么?在介紹JavaScript中的內(nèi)存之前,我們將簡(jiǎn)要討論內(nèi)存是什么以及它是如何工作的。
硬件層面上,計(jì)算機(jī)內(nèi)存由大量的觸發(fā)器緩存的。每個(gè)觸發(fā)器包含幾個(gè)晶體管,能夠存儲(chǔ)一位,單個(gè)觸發(fā)器都可以通過(guò)唯一標(biāo)識(shí)符尋址,因此我們可以讀取和覆蓋它們。因此,從概念上講,可以把的整個(gè)計(jì)算機(jī)內(nèi)存看作是一個(gè)可以讀寫(xiě)的巨大數(shù)組。
作為人類(lèi),我們并不擅長(zhǎng)用比特來(lái)思考和計(jì)算,所以我們把它們組織成更大的組,這些組一起可以用來(lái)表示數(shù)字。8位稱(chēng)為1字節(jié)。除了字節(jié),還有字(有時(shí)是16位,有時(shí)是32位)。
很多東西都存儲(chǔ)在內(nèi)存中:
程序使用的所有變量和其他數(shù)據(jù)。
程序的代碼,包括操作系統(tǒng)的代碼。
編譯器和操作系統(tǒng)一起為你處理大部分內(nèi)存管理,但是你還是需要了解一下底層的情況,對(duì)內(nèi)在管理概念會(huì)有更深入的了解。
在編譯代碼時(shí),編譯器可以檢查基本數(shù)據(jù)類(lèi)型,并提前計(jì)算它們需要多少內(nèi)存。然后將所需的大小分配給調(diào)用堆棧空間中的程序,分配這些變量的空間稱(chēng)為堆棧空間。因?yàn)楫?dāng)調(diào)用函數(shù)時(shí),它們的內(nèi)存將被添加到現(xiàn)有內(nèi)存之上,當(dāng)它們終止時(shí),它們按照后進(jìn)先出(LIFO)順序被移除。例如:
編譯器能夠立即知道所需的內(nèi)存:4 + 4×4 + 8 = 28字節(jié)。
這段代碼展示了整型和雙精度浮點(diǎn)型變量所占內(nèi)存的大小。但是大約20年前,整型變量通常占2個(gè)字節(jié),而雙精度浮點(diǎn)型變量占4個(gè)字節(jié)。你的代碼不應(yīng)該依賴(lài)于當(dāng)前基本數(shù)據(jù)類(lèi)型的大小。
編譯器將插入與操作系統(tǒng)交互的代碼,并申請(qǐng)存儲(chǔ)變量所需的堆棧字節(jié)數(shù)。
在上面的例子中,編譯器知道每個(gè)變量的確切內(nèi)存地址。事實(shí)上,每當(dāng)我們寫(xiě)入變量 n 時(shí),它就會(huì)在內(nèi)部被轉(zhuǎn)換成類(lèi)似“內(nèi)存地址4127963”這樣的信息。
注意,如果我們嘗試訪問(wèn) x[4],將訪問(wèn)與m關(guān)聯(lián)的數(shù)據(jù)。這是因?yàn)樵L問(wèn)數(shù)組中一個(gè)不存在的元素(它比數(shù)組中最后一個(gè)實(shí)際分配的元素x[3]多4字節(jié)),可能最終讀取(或覆蓋)一些 m 位。這肯定會(huì)對(duì)程序的其余部分產(chǎn)生不可預(yù)知的結(jié)果。
當(dāng)函數(shù)調(diào)用其他函數(shù)時(shí),每個(gè)函數(shù)在調(diào)用堆棧時(shí)獲得自己的塊。它保存所有的局部變量,但也會(huì)有一個(gè)程序計(jì)數(shù)器來(lái)記住它在執(zhí)行過(guò)程中的位置。當(dāng)函數(shù)完成時(shí),它的內(nèi)存塊將再次用于其他地方。
動(dòng)態(tài)分配不幸的是,當(dāng)編譯時(shí)不知道一個(gè)變量需要多少內(nèi)存時(shí),事情就有點(diǎn)復(fù)雜了。假設(shè)我們想做如下的操作:
在編譯時(shí),編譯器不知道數(shù)組需要使用多少內(nèi)存,因?yàn)檫@是由用戶(hù)提供的值決定的。
因此,它不能為堆棧上的變量分配空間。相反,我們的程序需要在運(yùn)行時(shí)顯式地向操作系統(tǒng)請(qǐng)求適當(dāng)?shù)目臻g,這個(gè)內(nèi)存是從堆空間分配的。靜態(tài)內(nèi)存分配和動(dòng)態(tài)內(nèi)存分配的區(qū)別總結(jié)如下表所示:
靜態(tài)內(nèi)存分配 | 動(dòng)態(tài)內(nèi)存分配 |
---|---|
大小必須在編譯時(shí)知道 | 大小不需要在編譯時(shí)知道 |
在編譯時(shí)執(zhí)行 | 在運(yùn)行時(shí)執(zhí)行 |
分配給堆棧 | 分配給堆 |
FILO (先進(jìn)后出) | 沒(méi)有特定的分配順序 |
要完全理解動(dòng)態(tài)內(nèi)存分配是如何工作的,需要在指針上花費(fèi)更多的時(shí)間,這可能與本文的主題有太多的偏離,這里就不太詳細(xì)介紹指針的相關(guān)的知識(shí)了。
在JavaScript中分配內(nèi)存現(xiàn)在將解釋第一步:如何在JavaScript中分配內(nèi)存。
JavaScript為讓開(kāi)發(fā)人員免于手動(dòng)處理內(nèi)存分配的責(zé)任——JavaScript自己進(jìn)行內(nèi)存分配同時(shí)聲明值。
某些函數(shù)調(diào)用也會(huì)導(dǎo)致對(duì)象的內(nèi)存分配:
方法可以分配新的值或?qū)ο?
在JavaScript中使用內(nèi)存在JavaScript中使用分配的內(nèi)存意味著在其中讀寫(xiě),這可以通過(guò)讀取或?qū)懭胱兞炕驅(qū)ο髮傩缘闹?,或者將參?shù)傳遞給函數(shù)來(lái)實(shí)現(xiàn)。
當(dāng)內(nèi)存不再需要時(shí)進(jìn)行釋放大多數(shù)的內(nèi)存管理問(wèn)題都出現(xiàn)在這個(gè)階段
這里最困難的地方是確定何時(shí)不再需要分配的內(nèi)存,它通常要求開(kāi)發(fā)人員確定程序中哪些地方不再需要內(nèi)存的并釋放它。
高級(jí)語(yǔ)言嵌入了一種稱(chēng)為垃圾收集器的機(jī)制,它的工作是跟蹤內(nèi)存分配和使用,以便發(fā)現(xiàn)任何時(shí)候一塊不再需要已分配的內(nèi)在。在這種情況下,它將自動(dòng)釋放這塊內(nèi)存。
不幸的是,這個(gè)過(guò)程只是進(jìn)行粗略估計(jì),因?yàn)楹茈y知道某塊內(nèi)存是否真的需要 (不能通過(guò)算法來(lái)解決)。
大多數(shù)垃圾收集器通過(guò)收集不再被訪問(wèn)的內(nèi)存來(lái)工作,例如,指向它的所有變量都超出了作用域。但是,這是可以收集的內(nèi)存空間集合的一個(gè)不足估計(jì)值,因?yàn)樵趦?nèi)存位置的任何一點(diǎn)上,仍然可能有一個(gè)變量在作用域中指向它,但是它將永遠(yuǎn)不會(huì)被再次訪問(wèn)。
垃圾收集由于無(wú)法確定某些內(nèi)存是否真的有用,因此,垃圾收集器想了一個(gè)辦法來(lái)解決這個(gè)問(wèn)題。本節(jié)將解釋理解主要垃圾收集算法及其局限性。
內(nèi)存引用垃圾收集算法主要依賴(lài)的是引用。
在內(nèi)存管理上下文中,如果對(duì)象具有對(duì)另一個(gè)對(duì)象的訪問(wèn)權(quán)(可以是隱式的,也可以是顯式的),則稱(chēng)對(duì)象引用另一個(gè)對(duì)象。例如,JavaScript對(duì)象具有對(duì)其原型(隱式引用)和屬性值(顯式引用)的引用。
在此上下文中,“對(duì)象”的概念被擴(kuò)展到比常規(guī)JavaScript對(duì)象更廣泛的范圍,并且還包含函數(shù)范圍(或全局詞法作用域)。
詞法作用域定義了如何在嵌套函數(shù)中解析變量名:即使父函數(shù)已經(jīng)返回,內(nèi)部函數(shù)也包含父函數(shù)的作用引用計(jì)數(shù)垃圾收集算法
這是最簡(jiǎn)單的垃圾收集算法。如果沒(méi)有指向?qū)ο蟮囊?,則認(rèn)為該對(duì)象是“垃圾可回收的”,如下代碼:
循環(huán)會(huì)產(chǎn)生問(wèn)題當(dāng)涉及到循環(huán)時(shí),會(huì)有一個(gè)限制。在下面的示例中,創(chuàng)建了兩個(gè)對(duì)象,兩個(gè)對(duì)象互相引用,從而創(chuàng)建了一個(gè)循環(huán)。在函數(shù)調(diào)用之后將超出作用域,因此它們實(shí)際上是無(wú)用的,可以被釋放。然而,引用計(jì)數(shù)算法認(rèn)為,由于每個(gè)對(duì)象至少被引用一次,所以它們都不能被垃圾收集。
標(biāo)記-清除(Mark-and-sweep)算法該算法能夠判斷出某個(gè)對(duì)象是否可以訪問(wèn),從而知道該對(duì)象是否有用,該算法由以下步驟組成:
垃圾收集器構(gòu)建一個(gè)“根”列表,用于保存引用的全局變量。在JavaScript中,“window”對(duì)象是一個(gè)可作為根節(jié)點(diǎn)的全局變量。
然后,算法檢查所有根及其子節(jié)點(diǎn),并將它們標(biāo)記為活動(dòng)的(這意味著它們不是垃圾)。任何根不能到達(dá)的地方都將被標(biāo)記為垃圾。
最后,垃圾收集器釋放所有未標(biāo)記為活動(dòng)的內(nèi)存塊,并將該內(nèi)存返回給操作系統(tǒng)。
這個(gè)算法比上一個(gè)算法要好,因?yàn)椤耙粋€(gè)對(duì)象沒(méi)有被引用”就意味著這個(gè)對(duì)象無(wú)法訪問(wèn)。
截至2012年,所有現(xiàn)代瀏覽器都有標(biāo)記-清除垃圾收集器。過(guò)去幾年在JavaScript垃圾收集(分代/增量/并發(fā)/并行垃圾收集)領(lǐng)域所做的所有改進(jìn)都是對(duì)該算法(標(biāo)記-清除)的實(shí)現(xiàn)改進(jìn),而不是對(duì)垃圾收集算法本身的改進(jìn),也不是它決定對(duì)象是否可訪問(wèn)的目標(biāo)。
在這篇文章中,你可以更詳細(xì)地閱讀到有關(guān)跟蹤垃圾收集的詳細(xì)信息,同時(shí)還包括了標(biāo)記-清除算法及其優(yōu)化。
循環(huán)不再是問(wèn)題在上面的第一個(gè)例子中,在函數(shù)調(diào)用返回后,這兩個(gè)對(duì)象不再被從全局對(duì)象中可訪問(wèn)的對(duì)象引用。因此,垃圾收集器將發(fā)現(xiàn)它們不可訪問(wèn)。
盡管對(duì)象之間存在引用,但它們對(duì)于根節(jié)點(diǎn)來(lái)說(shuō)是不可達(dá)的。
垃圾收集器的反直觀行為盡管垃圾收集器很方便,但它們有一套自己的折衷方案,其中之一就是非決定論,換句話(huà)說(shuō),GC是不可預(yù)測(cè)的,你無(wú)法真正判斷何時(shí)進(jìn)行垃圾收集。這意味著在某些情況下,程序會(huì)使用更多的內(nèi)存,這實(shí)際上是必需的。在對(duì)速度特別敏感的應(yīng)用程序中,可能會(huì)很明顯的感受到短時(shí)間的停頓。如果沒(méi)有分配內(nèi)存,則大多數(shù)GC將處于空閑狀態(tài)。看看以下場(chǎng)景:
分配一組相當(dāng)大的內(nèi)在。
這些元素中的大多數(shù)(或全部)被標(biāo)記為不可訪問(wèn)(假設(shè)引用指向一個(gè)不再需要的緩存)。
不再進(jìn)一步的分配
在這些場(chǎng)景中,大多數(shù)GCs 將不再繼續(xù)收集。換句話(huà)說(shuō),即使有不可訪問(wèn)的引用可供收集,收集器也不會(huì)聲明這些引用。這些并不是嚴(yán)格意義上的泄漏,但仍然會(huì)導(dǎo)致比通常更高的內(nèi)存使用。
內(nèi)存泄漏是什么?從本質(zhì)上說(shuō),內(nèi)存泄漏可以定義為:不再被應(yīng)用程序所需要的內(nèi)存,出于某種原因,它不會(huì)返回到操作系統(tǒng)或空閑內(nèi)存池中。
編程語(yǔ)言支持不同的內(nèi)存管理方式。然而,是否使用某一塊內(nèi)存實(shí)際上是一個(gè)無(wú)法確定的問(wèn)題。換句話(huà)說(shuō),只有開(kāi)發(fā)人員才能明確一塊內(nèi)存是否可以返回到操作系統(tǒng)。
某些編程語(yǔ)言為開(kāi)發(fā)人員提供了幫助,另一些則期望開(kāi)發(fā)人員能清楚地了解內(nèi)存何時(shí)不再被使用。維基百科上有一些有關(guān)人工和自動(dòng)內(nèi)存管理的很不錯(cuò)的文章。
四種常見(jiàn)的內(nèi)存泄漏 1.全局變量JavaScript以一種有趣的方式處理未聲明的變量: 對(duì)于未聲明的變量,會(huì)在全局范圍中創(chuàng)建一個(gè)新的變量來(lái)對(duì)其進(jìn)行引用。在瀏覽器中,全局對(duì)象是window。例如:
function foo(arg) { bar = "some text"; }
等價(jià)于:
function foo(arg) { window.bar = "some text"; }
如果bar在foo函數(shù)的作用域內(nèi)對(duì)一個(gè)變量進(jìn)行引用,卻忘記使用var來(lái)聲明它,那么將創(chuàng)建一個(gè)意想不到的全局變量。在這個(gè)例子中,遺漏一個(gè)簡(jiǎn)單的字符串不會(huì)造成太大的危害,但這肯定會(huì)很糟。
創(chuàng)建一個(gè)意料之外的全局變量的另一種方法是使用this:
function foo() { this.var1 = "potential accidental global"; } // Foo自己調(diào)用,它指向全局對(duì)象(window),而不是未定義。 foo();
可以在JavaScript文件的開(kāi)頭通過(guò)添加“use strict”來(lái)避免這一切,它將開(kāi)啟一個(gè)更嚴(yán)格的JavaScript解析模式,以防止意外創(chuàng)建全局變量。
盡管我們討論的是未知的全局變量,但仍然有很多代碼充斥著顯式的全局變量。根據(jù)定義,這些是不可收集的(除非被指定為空或重新分配)。用于臨時(shí)存儲(chǔ)和處理大量信息的全局變量特別令人擔(dān)憂(yōu)。如果你必須使用一個(gè)全局變量來(lái)存儲(chǔ)大量數(shù)據(jù),那么請(qǐng)確保將其指定為null,或者在完成后將其重新賦值。
2.被遺忘的定時(shí)器和回調(diào)以setInterval為例,因?yàn)樗贘avaScript中經(jīng)常使用。
var serverData = loadData(); setInterval(function() { var renderer = document.getElementById("renderer"); if(renderer) { renderer.innerHTML = JSON.stringify(serverData); } }, 5000); //每五秒會(huì)執(zhí)行一次
上面的代碼片段演示了使用定時(shí)器時(shí)引用不再需要的節(jié)點(diǎn)或數(shù)據(jù)。
renderer表示的對(duì)象可能會(huì)在未來(lái)的某個(gè)時(shí)間點(diǎn)被刪除,從而導(dǎo)致內(nèi)部處理程序中的一整塊代碼都變得不再需要。但是,由于定時(shí)器仍然是活動(dòng)的,所以,處理程序不能被收集,并且其依賴(lài)項(xiàng)也無(wú)法被收集。這意味著,存儲(chǔ)著大量數(shù)據(jù)的serverData也不能被收集。
在使用觀察者時(shí),您需要確保在使用完它們之后進(jìn)行顯式調(diào)用來(lái)刪除它們(要么不再需要觀察者,要么對(duì)象將變得不可訪問(wèn))。
作為開(kāi)發(fā)者時(shí),需要確保在完成它們之后進(jìn)行顯式刪除它們(或者對(duì)象將無(wú)法訪問(wèn))。
在過(guò)去,一些瀏覽器無(wú)法處理這些情況(很好的IE6)。幸運(yùn)的是,現(xiàn)在大多數(shù)現(xiàn)代瀏覽器會(huì)為幫你完成這項(xiàng)工作:一旦觀察到的對(duì)象變得不可訪問(wèn),即使忘記刪除偵聽(tīng)器,它們也會(huì)自動(dòng)收集觀察者處理程序。然而,我們還是應(yīng)該在對(duì)象被處理之前顯式地刪除這些觀察者。例如:
如今,現(xiàn)在的瀏覽器(包括IE和Edge)使用現(xiàn)代的垃圾回收算法,可以立即發(fā)現(xiàn)并處理這些循環(huán)引用。換句話(huà)說(shuō),在一個(gè)節(jié)點(diǎn)刪除之前也不是必須要調(diào)用removeEventListener。
一些框架或庫(kù),比如JQuery,會(huì)在處置節(jié)點(diǎn)之前自動(dòng)刪除監(jiān)聽(tīng)器(在使用它們特定的API的時(shí)候)。這是由庫(kù)內(nèi)部的機(jī)制實(shí)現(xiàn)的,能夠確保不發(fā)生內(nèi)存泄漏,即使在有問(wèn)題的瀏覽器下運(yùn)行也能這樣,比如……IE 6。
3.閉包閉包是javascript開(kāi)發(fā)的一個(gè)關(guān)鍵方面,一個(gè)內(nèi)部函數(shù)使用了外部(封閉)函數(shù)的變量。由于JavaScript運(yùn)行的細(xì)節(jié),它可能以下面的方式造成內(nèi)存泄漏:
這段代碼做了一件事:每次調(diào)用replaceThing的時(shí)候,theThing都會(huì)得到一個(gè)包含一個(gè)大數(shù)組和一個(gè)新閉包(someMethod)的新對(duì)象。同時(shí),變量unused指向一個(gè)引用了`originalThing的閉包。
是不是有點(diǎn)困惑了? 重要的是,一旦具有相同父作用域的多個(gè)閉包的作用域被創(chuàng)建,則這個(gè)作用域就可以被共享。
在這種情況下,為閉包someMethod而創(chuàng)建的作用域可以被unused共享的。unused內(nèi)部存在一個(gè)對(duì)originalThing的引用。即使unused從未使用過(guò),someMethod也可以在replaceThing的作用域之外(例如在全局范圍內(nèi))通過(guò)theThing來(lái)被調(diào)用。
由于someMethod共享了unused閉包的作用域,那么unused引用包含的originalThing會(huì)迫使它保持活動(dòng)狀態(tài)(兩個(gè)閉包之間的整個(gè)共享作用域)。這阻止了它被收集。
當(dāng)這段代碼重復(fù)運(yùn)行時(shí),可以觀察到內(nèi)存使用在穩(wěn)定增長(zhǎng),當(dāng)GC運(yùn)行后,內(nèi)存使用也不會(huì)變小。從本質(zhì)上說(shuō),在運(yùn)行過(guò)程中創(chuàng)建了一個(gè)閉包鏈表(它的根是以變量theThing的形式存在),并且每個(gè)閉包的作用域都間接引用了了一個(gè)大數(shù)組,這造成了相當(dāng)大的內(nèi)存泄漏。
4.脫離DOM的引用有時(shí),將DOM節(jié)點(diǎn)存儲(chǔ)在數(shù)據(jù)結(jié)構(gòu)中可能會(huì)很有用。假設(shè)你希望快速地更新表中的幾行內(nèi)容,那么你可以在一個(gè)字典或數(shù)組中保存每個(gè)DOM行的引用。這樣,同一個(gè)DOM元素就存在兩個(gè)引用:一個(gè)在DOM樹(shù)中,另一個(gè)則在字典中。如果在將來(lái)的某個(gè)時(shí)候你決定刪除這些行,那么你需要將這兩個(gè)引用都設(shè)置為不可訪問(wèn)。
在引用 DOM 樹(shù)中的內(nèi)部節(jié)點(diǎn)或葉節(jié)點(diǎn)時(shí),還需要考慮另外一個(gè)問(wèn)題。如果在代碼中保留對(duì)表單元格的引用(
你可能認(rèn)為垃圾收集器將釋放除該單元格之外的所有內(nèi)容。然而,事實(shí)并非如此,由于單元格是表的一個(gè)子節(jié)點(diǎn),而子節(jié)點(diǎn)保存對(duì)父節(jié)點(diǎn)的引用,所以對(duì)表單元格的這個(gè)引用將使整個(gè)表保持在內(nèi)存中,所以在移除有被引用的節(jié)點(diǎn)時(shí)候要移除其子節(jié)點(diǎn)。
編輯中可能存在的bug沒(méi)法實(shí)時(shí)知道,事后為了解決這些bug,花了大量的時(shí)間進(jìn)行l(wèi)og 調(diào)試,這邊順便給大家推薦一個(gè)好用的BUG監(jiān)控工具Fundebug。
原文:https://blog.sessionstack.com...
你的點(diǎn)贊是我持續(xù)分享好東西的動(dòng)力,歡迎點(diǎn)贊!
交流干貨系列文章匯總?cè)缦拢X(jué)得不錯(cuò)點(diǎn)個(gè)Star,歡迎 加群 互相學(xué)習(xí)。
https://github.com/qq44924588...
我是小智,公眾號(hào)「大遷世界」作者,對(duì)前端技術(shù)保持學(xué)習(xí)愛(ài)好者。我會(huì)經(jīng)常分享自己所學(xué)所看的干貨,在進(jìn)階的路上,共勉!
關(guān)注公眾號(hào),后臺(tái)回復(fù)福利,即可看到福利,你懂的。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/100081.html
摘要:是如何工作的內(nèi)存管理以及如何處理四種常見(jiàn)的內(nèi)存泄漏原文譯者幾個(gè)禮拜之前我們開(kāi)始一系列對(duì)于以及其本質(zhì)工作原理的深入挖掘我們認(rèn)為通過(guò)了解的構(gòu)建方式以及它們是如何共同合作的,你就能夠?qū)懗龈玫拇a以及應(yīng)用。 JavaScript是如何工作的:內(nèi)存管理以及如何處理四種常見(jiàn)的內(nèi)存泄漏 原文:How JavaScript works: memory management + how to han...
摘要:本文作為第三篇,將會(huì)討論另一個(gè)開(kāi)發(fā)者容易忽視的重要主題內(nèi)存管理。我們也會(huì)提供一些關(guān)于如何處理內(nèi)存泄露的技巧。這是當(dāng)前整型和雙精度的大小。然而,這是一組可以收集的內(nèi)存空間的近似值。 本文轉(zhuǎn)載自:眾成翻譯譯者:Leslie Wang審校: 為之漫筆鏈接:http://www.zcfy.cc/article/4211原文:https://blog.sessionstack.com/how-j...
摘要:本文將會(huì)討論中的內(nèi)存泄漏以及如何處理,方便大家在使用編碼時(shí),更好的應(yīng)對(duì)內(nèi)存泄漏帶來(lái)的問(wèn)題。當(dāng)內(nèi)存不再需要時(shí)進(jìn)行釋放大部分內(nèi)存泄漏問(wèn)題都是在這個(gè)階段產(chǎn)生的,這個(gè)階段最難的問(wèn)題就是確定何時(shí)不再需要已分配的內(nèi)存。中的相同對(duì)象稱(chēng)為全局。 隨著現(xiàn)在的編程語(yǔ)言功能越來(lái)越成熟、復(fù)雜,內(nèi)存管理也容易被大家忽略。本文將會(huì)討論JavaScript中的內(nèi)存泄漏以及如何處理,方便大家在使用JavaScript...
摘要:這是因?yàn)槲覀冊(cè)L問(wèn)了數(shù)組中不存在的數(shù)組元素它超過(guò)了最后一個(gè)實(shí)際分配到內(nèi)存的數(shù)組元素字節(jié),并且有可能會(huì)讀取或者覆寫(xiě)的位。包含個(gè)元素的新數(shù)組由和數(shù)組元素所組成中的內(nèi)存使用中使用分配的內(nèi)存主要指的是內(nèi)存讀寫(xiě)。 原文請(qǐng)查閱這里,本文有進(jìn)行刪減,文后增了些經(jīng)驗(yàn)總結(jié)。 本系列持續(xù)更新中,Github 地址請(qǐng)查閱這里。 這是 JavaScript 工作原理的第三章。 我們將會(huì)討論日常使用中另一個(gè)被開(kāi)發(fā)...
閱讀 1398·2019-08-30 12:54
閱讀 1880·2019-08-30 11:16
閱讀 1623·2019-08-30 10:50
閱讀 2459·2019-08-29 16:17
閱讀 1277·2019-08-26 12:17
閱讀 1388·2019-08-26 10:15
閱讀 2398·2019-08-23 18:38
閱讀 795·2019-08-23 17:50