摘要:的內存管理是那些被稱作垃圾回收語言當中的一員。垃圾回收與內存泄漏垃圾回收,簡稱。反例循環多次觸發,效率太低在舊的瀏覽器上會導致內存泄漏正解綁定事件的元素是不能在時被清理的,應該在之前取消事件綁定。
首先我們需要理解,內存是什么。簡單來講,內存存儲了計算機運行過程的需要的全部數據,也就是計算機正在使用的全部數據。我們需要合理的使用內存,防止內存被大量無用數據占用,同時也要防止訪問和修改與當前程序無關的內存區域。內存主要包括以下幾個部分: 內核數據區域,棧區,共享庫映像,堆區,可讀寫區域,只讀區域。學習javascript,我們不需要理解內存和cache,內存和I/O之間具體工作原理,但我們需要了解掌握如何合理的使用內存,合理的分配釋放內存。
javascript的內存管理Javascript 是那些被稱作垃圾回收語言當中的一員。垃圾回收語言通過周期性地檢查那些之前被分配出去的內存是否可以從應用的其他部分訪問來幫助開發者管理內存。換句話說,當計算機發現有的內存已經不能被訪問到了,就會把它們標記為垃圾。開發者只需要知道一塊已分配的內存是否會在將來被使用,而不可訪問的內存可以通過算法確定并標記以便返還給操作系統。
引用傳遞和值傳遞js中的變量除了6個基本類型以外,其余的都是對象。也就說基本類型在賦值是傳遞的是值,也就是原來數據的一份拷貝?;绢愋桶╪umber、string、boolean、symbol、null、undefined.
用2個例子來理解一下:
var a = 10; //基本類型 var b = a; //a把10拷貝一份,把這個拷貝給b a = 20; //修改了a,不影響a的拷貝 console.log(a); //20 console.log(b); //10引用傳遞
var a = {num: 20}; //不是基本類型 var b = a; //這里沒有任何拷貝工作,b指向和a完全一致的同一塊內存 b.num = 15; //由于b和a指向同一塊內存,所以b.num修改了等同于a.num修改了 console.log(a.num); //15 console.log(b.num); //15 //進一步理解 b = {age: 10}; //等號右邊定義了一個新的對象,產生的新的內存分配,此時b指向了這塊新的內存,a還是指向原來那塊內存 console.log(a); //{num: 15} console.log(b); //{age: 10}
值得強調的是:在函數參數傳遞的時候和返回值時一樣遵守這個傳遞規則,這是構成閉包的基礎條件
簡單的函數傳遞參數//一個反例 var num1 = 10; var num2 = 20; function swap(a, b){ var temp = a; a = b; b = temp; } swap(num1, num2); console.log(num1); //10 console.log(num2); //20
以上代碼不能如愿的把2個傳入變量的值交換,因為基本類型在參數傳遞時也是值傳遞,及a,b是num1,num2的拷貝,不是num1和num2本身。當然實現交換的方法很多,在不引入第三個變量情況下,不用多帶帶寫一個函數。
//實現交換a,b //方法1: var temp = a; a = b; b =temp; //方法2: a = a + b; b = a - b; a = a - b; //方法3: a = [b, b = a][0]; //方法4(僅適用于整數交換): a = a ^ b; // ^表示異或運算 b = a ^ b; a = a ^ b; //方法5: [a, b] = [b, a]; //解構賦值閉包的原理
var inc = function(){ var x = 0; return function(){ //返回一個非基本類型 console.log(x++); }; }; inc1 = inc(); //inc1是閉包內匿名函數的引用,由于該引用存在,匿名函數引用計數不為0,所以inc作用域對應的內存不能釋放,閉包形成 inc1(); //0 inc1(); //1淺拷貝與深拷貝
當對象的屬性是對象的時候,簡單地賦值導致改屬性傳遞的是另一個對象屬性的引用,這樣的拷貝是淺拷貝,存在安全風險。我們應該遞歸的拷貝對象屬性的每個對象,形成深拷貝。方法如下:
//淺拷貝與深拷貝 var o = { name: "Lily", age: 10, addr:{ city: "Shenzheng", province: "Guangdong" }, schools: ["primaryS", "middleS", "heightS"] }; var newOne = copy(o); console.log(o); //Object {name: "Lily", age: 10, addr: Object} console.log(newOne); //Object {name: "Lily", age: 10, addr: Object} newOne.name = "Bob"; console.log(newOne.name); //"Bob" console.log(o.name); //"Lily" newOne.addr.city = "Foshan"; console.log(newOne.addr.city); //"Foshan" console.log(o.addr.city); //"Foshan" function copy(obj){ var obj = obj || {}; var newObj = {}; for(prop in obj){ if(!obj.hasOwnProperty(prop)) continue; newObj[prop] = obj[prop]; //當obj[prop]不是基本類型的時候,這里傳的時引用 } return newObj; } var newOne = deepCopy(o); console.log(o); //Object {name: "Lily", age: 10, addr: Object} console.log(newOne); //Object {name: "Lily", age: 10, addr: Object} newOne.name = "Bob"; console.log(newOne.name); //"Bob" console.log(o.name); //"Lily" newOne.addr.city = "Foshan"; console.log(newOne.addr.city); //"Foshan" console.log(o.addr.city); //"Shenzheng" newOne.schools[0] = "primatrySchool"; console.log(newOne.schools[0]); //"primatrySchool" console.log(o.schools[0]); //"primatryS" function deepCopy(obj){ var obj = obj || {}; var newObj = {}; deeply(obj, newObj); function deeply(oldOne, newOne){ for(var prop in oldOne){ if(!oldOne.hasOwnProperty(prop)) continue; if(typeof oldOne[prop] === "object" && oldOne[prop] !== null){ newOne[prop] = oldOne[prop].constructor === Array ? [] : {}; deeply(oldOne[prop], newOne[prop]); } else newOne[prop] = oldOne[prop]; } } return newObj; }變量定義和內存釋放
不同的變量定義方式,會導致變量不能被刪除,內存無法釋放。
// 定義三個全局變量 var global_var = 1; global_novar = 2; // 反面教材 (function () { global_fromfunc = 3; // 反面教材 }()); // 試圖刪除 delete global_var; // false delete global_novar; // true delete global_fromfunc; // true // 測試該刪除 typeof global_var; // "number" typeof global_novar; // "undefined" typeof global_fromfunc; // "undefined"
很明顯,通過var定義的變量無法被釋放。
垃圾回收與內存泄漏垃圾回收(Garbage Collection),簡稱GC。簡單來講,GC就是把內存中不需要的數據釋放了,這樣這部分內存就可以存放其他東西了。在javascript中,如果一個對象不再被引用,那么這個對象就會被GC回收。具體回收策略包括以下3種:
標記回收當從window節點遍歷DOM樹不能遍歷到某個對象,那么這個對象就會被標記為沒用的對象。由于回收機制是周期性執行的,這樣,當下一個回收周期到來時,這個對象對應的內存就會被釋放。
引用計數當系統中定義了一個對象后,對于這一塊內存,javascript會記錄有多少個引用指向個部分內存,如果這個數為零,則這部分內存會在下一個回收周期被釋放。
手動釋放就好比上一個例子中,利用delete關鍵字刪除變量或屬性,達到釋放內存的目的。分一下幾種情況:
//釋放一個對象 obj = null; //釋放是個對象屬性 delete obj.propertyName; delete globalVariable; //沒有用var聲明的變量是window的屬性,用delete釋放。 //釋放數組 array.length = 0; //釋放數組元素 array.splice(2,2); //刪除并釋放第三個元素起的2個元素
不過需要注意的是, 這幾個GC策略是同時作用的:
var o1 = {}; //開辟一塊內存放置對象,并用o1指向它 var o2 = o1; //o2指向與o1同一個內存區域 console.log(o1); //{} console.log(o2); //{} o2 = null; //標記o2為沒用的對象 console.log(o2); //null console.log(o1); //{} 由于還有o1指向這個內存區域,引用計數不為零,所以內存并沒有被釋放 o1 = null; //引用計數為0, 內存釋放
如果你訪問了已經被回收了的內存,會發生不可預計的嚴重后果。比如一段內存被釋放了,可能里面的值就不是原來的值了,你還要拿來用那不是自己找錯誤嗎?更嚴重的就是你修改了其他程序的數據!??!我們將這樣的變量叫做野指針(wild pointer)。為了避免這樣的也只能出現,也為了節省計算機資源,我們需要防止內存泄露(memory leak)。
內存泄漏也稱作存儲滲漏,用動態存儲分配函數動態開辟的空間,在使用完畢后未釋放,結果導致一直占據該內存單元,直到程序結束。簡單來說就是該內存空間使用完畢之后未回收。
內存泄露是每個開發者最終都不得不面對的問題。即便使用自動內存管理的語言,你還是會碰到一些內存泄漏的情況。內存泄露會導致一系列問題,比如:運行緩慢,崩潰,高延遲,甚至一些與其他應用相關的問題。
可能導致內存泄漏的操作清除所以子元素用innerHTML=""替代removeChild(),因為在sIEve中監測的結果是用removeChild無法有效地釋放dom節點。
//反例 var parent = document.getElementById("parent"); var first = parent.firstChild(); while(first){ //循環多次觸發reflow,效率太低 parent.removeChild(first); //在舊的瀏覽器上會導致內存泄漏 first = parent.firstChild(); } //正解 document.getElementById("parent").innerHTML = “”;
綁定事件的元素是不能在remove時被清理的,應該在remove之前取消事件綁定。不過更好的辦法是用事件委托的方式綁定事件。
var ele = document.getElementById("eleID"); ele.onclick = function fun(){ //Do stuff here } //... ele.onclick = null; //刪除元素前取消所有事件,jQuery中也是在刪除節點前利用removeEventListen去除了對應事件 ele.parentNode.removeChild(ele);
意外的全局變量,會使得實際函數結束就應該釋放的內存保留下來,造成資源浪費,包括以下兩種情況:
在嚴格模式下編寫代碼可以避免這個問題
//情況一: 函數中沒有用var聲明的變量 function fun1(){ name = "Mary"; //全局變量 } fun1(); //情況二: 構造函數沒用new關鍵字調用 function Person(name){ this.name = name; } Person("Mary"); //函數內定義全局變量
定時器中的變量定義,會在每次執行函數的時候重復定義變量,產生嚴重的內存泄漏。
//反例 setInterval(function(){ var ele = document.getElementById("eleID"); //改代碼毎100毫秒會重復定義該引用 //Do stuff }, 100); //正解 setInterval(function(){ var ele = document.getElementById("eleID"); //改代碼毎100毫秒會重復定義該引用 //Do stuff ele = null; }, 100);
如果閉包的作用域鏈中保存著一個DOM對象或者ActiveX對象,那么就意味著該元素將無法被銷毀:
//反例 //不妨認為這里的上下文是window function init(){ var el = document.getElementById("MyElement"); //這是一個DOM元素的引用,非基本類型 el.onclick = function(){ //el.onclick是function匿名函數的引用 alert(el.innerHTML); //funciton中訪問了這個作用域以外的DOM元素引用el,導致el不能被釋放 } } init(); //正解 function init(){ var el = document.getElementById("MyElement"); //這是一個DOM元素的引用,是非基本類型 var text = el.innerHTML; //字符串,是基本類型,解決alert(el.innerHTML)不能正常工作問題 el.onclick = function(){ //el.onclick是function匿名函數的引用 alert(text); //var聲明的text是基本類型,不必釋放 } el = null; //手動釋放,但會導致alert(el.innerHTML)不能正常工作 } init(); //如果函數結尾要return el,用以下方法釋放el //正解 function init(){ var el = document.getElementById("MyElement"); //這是一個DOM元素的引用,是非基本類型 var text = el.innerHTML; //字符串,是基本類型,解決alert(el.innerHTML)不能正常工作問題 el.onclick = function(){ //el.onclick是function匿名函數的引用 alert(text); //var聲明的text是基本類型,不必釋放 } try{ return el; } finally { el = null; //手動釋放,但會導致alert(el.innerHTML)不能正常工作 } } init();
通過createElement,createTextNode等方法創建的元素會在寫入DOM后被釋放
function create() { var parent = document.getElementById("parent"); for (var i = 0; i < 5000; i++) { var el = document.createElement("div"); el.innerHTML = "test"; gc.appendChild(el); //這里釋放了內存 } }
循環引用導致引用計數永遠不為0,內存無法釋放:
//構成一個循環引用 var o1 = {name: "o1"}; var o2 = {name: "o2"}; o1.pro = o2; o2.pro = o1; //這種情況需要手動清理內存,在不需要的時候把對象置為null或刪除pro屬性
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/97463.html
摘要:內存回收此時,局部變量就沒有存在的必要了,因此可以釋放它們的內存以供將來使用。局部變量會在它們離開執行環境時自動被解除引用,如下面這個例子所示手工解除的引用由于局部變量在函數執行完畢后就離開了其執行環境,因此無需我們顯式地去為它解除引用。 JavaScript 具有自動垃圾收集機制(GC:Garbage Collecation),也就是說,執行環境會負責管理代碼執行過程中使用的內存。而...
摘要:摘要是如何回收內存的深入淺出系列深入淺出第課箭頭函數中的究竟是什么鬼深入淺出第課函數是一等公民是什么意思呢深入淺出第課什么是垃圾回收算法最近垃圾回收這個話題非?;?,大家不能隨隨便便的扔垃圾了,還得先分類,這樣方便對垃圾進行回收再利用。 摘要: JS是如何回收內存的? 《JavaScript深入淺出》系列: JavaScript深入淺出第1課:箭頭函數中的this究竟是什么鬼? Jav...
摘要:本文詳細描述了堆內存模型,垃圾回收算法以及處理內存泄露的最佳方案,并輔之以圖表,希望能對理解內存結構有所幫助。該區域也稱為內存模型的本地區。在中,內存泄露是指對象已不再使用,但垃圾回收未能將他們視做不使用對象予以回收。 本文詳細描述了 Java 堆內存模型,垃圾回收算法以及處理內存泄露的最佳方案,并輔之以圖表,希望能對理解 Java 內存結構有所幫助。原文作者 Sumith Puri,...
摘要:的內存限制和垃圾回收機制內存限制內存限制一般的后端語言開發中,在基本的內存使用是沒有限制的。的內存分代目前沒有一種垃圾自動回收算法適用于所有場景,所以的內部采用的其實是兩種垃圾回收算法。 前言 從前端思維轉變到后端, 有一個很重要的點就是內存管理。以前寫前端因為只是在瀏覽器上運行, 所以對于內存管理一般不怎么需要上心, 但是在服務器端, 則需要斤斤計較內存。 V8的內存限制和垃圾回收機...
摘要:垃圾回收內存管理實踐先通過一個來看看在中進行垃圾回收的過程是怎樣的內存泄漏識別在環境里提供了方法用來查看當前進程內存使用情況,單位為字節中保存的進程占用的內存部分,包括代碼本身棧堆。 showImg(https://segmentfault.com/img/remote/1460000019894672?w=640&h=426);作者 | 五月君Node.js 技術棧 | https:...
摘要:內存泄露內存泄露概念在計算機科學中,內存泄漏指由于疏忽或錯誤造成程序未能釋放已經不再使用的內存。判斷內存泄漏,以字段為準。 本文是 重溫基礎 系列文章的第二十二篇。 今日感受:優化學習方法。 系列目錄: 【復習資料】ES6/ES7/ES8/ES9資料整理(個人整理) 【重溫基礎】1-14篇 【重溫基礎】15.JS對象介紹 【重溫基礎】16.JSON對象介紹 【重溫基礎】1...
閱讀 955·2023-04-25 23:54
閱讀 3036·2021-11-08 13:21
閱讀 3759·2021-09-27 13:35
閱讀 3381·2021-07-26 23:41
閱讀 1043·2019-08-30 15:52
閱讀 3431·2019-08-30 11:27
閱讀 2088·2019-08-29 18:37
閱讀 528·2019-08-29 17:24