
內存管理C 語言這樣的底層語言一般都有底層的內存管理接口比如 malloc()和free()。而 JavaScript 是在創建變量對象字符串等時自動進行了分配內存并且在不使用它們時“自動”釋放。釋放的過程稱為垃圾回收。整個過程為分配內存 - 使用內存 - 清理內存。程序的運行需要內存。只要程序提出要求操作系統或者運行時(runtime)就必須供給內存。對于持續運行的服務進程(daemon)必須及時釋放不再用到的內存。否則內存占用越來越高輕則影響系統性能重則導致進程崩潰。也就是說不再用到的內存如果沒有及時釋放就叫做內存泄漏。JavaScript 在定義變量或者函數調用時就完成了內存分配varn123;// 給數值變量分配內存varo{a:1,b:null,};// 給對象及其包含的值分配內存// 給數組及其包含的值分配內存就像對象一樣vara[1,null,abra];functionf(a){returna2;}// 給函數可調用的對象分配內存// 函數表達式也能分配一個對象someElement.addEventListener(click,function(){someElement.style.backgroundColorblue;},false,);vardnewDate();// 分配一個 Date 對象varedocument.createElement(div);// 分配一個 DOM 元素V8 引擎內存圖新生代空間(New Space/Young Generation)作用主要用于存放生命周期短的小對象。這部分空間較小但對象的創建和銷毀都非常頻繁。 組成新生代內存被分為兩個半空間From Space和To Space。. √初始時對象被分配到From Space中。 √使用復制算法(Copying Garbage Collection)進行垃圾回收。 √當進行垃圾回收時活動的對象即仍然被引用的對象)被復制到To Space中而非活動的對象不再被引用的對象)被丟棄。 √完成復制后From Space和To Space的角色互換新的對象將分配到新的From Space中原To Space成為新的From Space。老生代空間(Old Space/Old Generation)作用存放生命周期長或從新生代晉升過來的對象。 √當對象在新生代中經歷了一定數量的垃圾回收周期后通常是一到兩次且仍然存活它們被認為是生命周期較長的對象。 分為兩個主要區域 √老指針空間(Old Pointer Space):主要存放包含指向其他對象的指針的對象。 √老數據空間(Old Data Space):用于存放只包含原始數據如數值、字符串的對象不含指向其他對象的指針。大對象空間(Large Object Space):用于存放大對象如超過新生代大小限制的數組或對象。這些對象直接在大對象空間中分配避免在新生代和老生代之間的復制操作。代碼空間(Code Space):存放編譯后的函數代碼。單元空間(Cell Space)用于存放小的數據結構比如閉包的變量環境。屬性單元空間(Property Cell Space):存放對象的屬性值主要針對全局變量或者屬性值對于訪問頻繁的全局變量或者屬性值來說V8在這里存儲是為了提高它的訪問效率。映射空間(Map Space):存放對象的映射即對象的類型信息描述對象的結構。當你定義一個Person構造函數時可以通過它創建出來oerson1和oerson2。這些實例(person1)和person2)本身存儲在堆內存的相應空間中具體是新生代還是老生代取決于它們的生命周期和大小。每個實例都會持有一個指向其映射的指針這個映射指明了如何訪問name和age屬性目的是訪問屬性效果變高)。堆內存(Heap Memory)與棧(Stack)堆內存JavaScript對象、字符串等數據存放的區域按照上述分類進行管理。棧用于存放執行上下文中的變量、函數調用的返回地址繼續執行哪里的代碼)等棧有助于跟蹤函數調用的順序和局部變量。面試題V8引擎的垃圾回收機制具體是如何工作的V8引擎使用了一種高度優化的垃圾回收機制來管理內存采用了標記清除、標記整理同時又結合了多種策略來實現高效的內存管理包括結合了分代回收(Generational Collection)和增量回收(Incremental Collection)等多種策略。分代回收V8將對象分為“新生代”和“老生代”。新生代存放生命周期短的小對象使用高效的復制式垃圾回收算法而老生代存放生命周期長或從新生代晉升而來的對象使用標記-清除或標記整理算法。這種分代策略減少了垃圾回收的總體開銷尤其是針對短命對象的快速回收。增量回收為了減少垃圾回收過程中的停頓時間V8實現了增量回收。這意味著垃圾回收過程被分解為許多小步驟這些小步驟穿插在應用程序的執行過程中進行。這有助于避免長時間的停頓改善了應用程序的響應性和性能。延遲清理和空閑時間收集V8還嘗試在CPU空閑時進行垃圾回收以進一步減少對程序執行的影響。這些技術的結合使得V8能夠在執行]avaScript代碼時有效地管理內存同時最小化垃圾回收對性能的影響。面試題有哪些操作可能引起內存泄漏如何在開發中避免性能優化內存泄漏通常是指程序中已經不再需要使用的內存由于某些原因未被垃圾回收器回收從而導致可用內存逐漸減少。全局變量濫用創建的全局變量例如忘記使用var,Iet,或const聲明變量可能會導致這些變量不被回收。未及時清理的定時器和回調函數比如使用setInterval在不適用時沒有及時清除阻止它們被回收。閉包閉包可以維持對外部函數作用域的引用如果這些閉包一直存活它們引用的外部作用域及其變量也無法被回收。DOM 引用JavaScript對象持有已從DOM中刪除的元素的引用這會阻止這些DOM元素的內存被釋放。監聽器的回調在使用完畢后沒有從DOM元素上移除事件監聽器這可能導致內存泄漏。面試題垃圾回收機制js 具有自動垃圾回收機制GC。垃圾回收器會定期周期性找出不在繼續使用的變量然后釋放其內存防止內存泄漏管理內存資源。在運行時垃圾回收機制主要通過追蹤每個對象的生命周期來工作。原理對象通常在它們不再被程序的任何部分引用時被視為垃圾。一旦這些對象被識別垃圾回收器將自動回收它們占用的內存空間使這部分內存可以重新被分配和使用。垃圾回收機制有幾種不同的實現方法最常見的包括引用計數每個對象都有一個與之關聯的計數器記錄引用該對象的次數。當引用計數變為零時意味著沒有任何引用指向該對象因此可以安全地回收其內存。標記-清除這種方法通過從根對象集合開始標記所有可達的對象。所有未被標記的對象都被視為垃圾并將被清除。標記整理與標記-清除相似但在清除階段它還會移動存活的對象以減少內存碎片。1. 引用計數每個對象都有一個關聯的計數器通常稱為“引用計數”。當一個對象有一個引用指向它時那么這個對象的引用就1如果另一個變量也開始引用該對象引用計數加1如果一個變量停止引用該對象引用計數減1。當一個對象的引用為0時這個對象就可以被銷毀掉varo{a:{b:2,},};// 兩個對象被創建一個作為另一個的屬性被引用另一個被分配給變量 o// 很顯然沒有一個可以被垃圾收集varo2o;// o2 變量是第二個對“這個對象”的引用o1;// 現在“這個對象”只有一個 o2 變量的引用了“這個對象”的原始引用 o 已經沒有varoao2.a;// 引用“這個對象”的 a 屬性// 現在“這個對象”有兩個引用了一個是 o2一個是 oao2yo;// 雖然最初的對象現在已經是零引用了可以被垃圾回收了// 但是它的屬性 a 的對象還在被 oa 引用所以還不能回收oanull;// a 屬性的那個對象現在也是零引用了// 它可以被垃圾回收了存在的問題循環引用引用計數算法考慮到它們互相都有至少一次引用所以它們不會被回收。當讓我們可以通過 WeakMap 弱引用解決。functionf(){varo{};varo2{};o.ao2;// o 引用 o2o2.ao;// o2 引用 oreturnazerty;}f();2. 標記清除過程分為標記 - 清除。標記清除的核心思路是可達性(Reachability)這個算法是設置一個根對象(root object),垃圾回收器會定期從這個根開始找所有從根開始有引用到的對象對于哪些沒有引用到的對象就認為是不可用的對象在這個階段垃圾回收器標記所有可達的對象之后垃圾回收器遍歷所有的對象收集那些在標記階段未被標記為可達的對象。這些對象被視為垃圾因為它們不再被程序中的其他活躍對象或根對象所引用。這個算法可以很好的解決循環引用的問題functiontest(){vara10// 被標記 進入環境varb20// 被標記 進入環境}test()// 執行完畢 之后 a b 又被標記 離開環境垃圾回收器在運行的時候會給存儲在內存中的所有變量都加上標記當然可以使用任何標記方式。然后它會去掉環境中的變量以及被環境中的變量引用的變量的標記閉包。而在此之后再被加上標記的變量將被視為準備刪除的變量原因是環境中的變量已經無法訪問到這些變量了。最后垃圾回收器完成內存清除工作銷毀那些帶標記的值并回收它們所占用的內存空間。其他垃圾回收算法js 引擎比較廣泛的采用的就是可達性中的標記清除算法當然類似于V8引擎為了進行更好的優化它在算法的實現細節上也會結合一些其他的算法。標記整理(Mark-Compact)和“標記-清除”相似不同的是回收期間同時會將保留的存儲對象搬運匯集到連續的內存空間從而整合空閑空間避免內存碎片化分代收集(Generational collection)一一對象被分成兩組“新的”和“I舊的”。許多對象出現完成它們的工作并很快“死去”它們可以很快被清理那些長期存活的對象會變得“老舊”而且被檢查的頻次也會減少增量收集(Incremental collection)如果有許多對象并且我們試圖一次遍歷并標記整個對象集則可能需要一些時間并在執行過程中帶來明顯的延遲。所以引擎試圖將垃圾收集工作分成幾部分來做分解成多個小步驟或“增量”然后將這幾部分會逐一進行處理這樣會有許多微小的延遲而不是一個大的延遲閑時收集(ldle-time collection)垃圾收集器只會在 CPU 空閑時嘗試運行以減少可能對代碼執行的影響。