国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

【重溫基礎】22.內存管理

Pandaaa / 960人閱讀

摘要:內存泄露內存泄露概念在計算機科學中,內存泄漏指由于疏忽或錯誤造成程序未能釋放已經不再使用的內存。判斷內存泄漏,以字段為準。

本文是 重溫基礎 系列文章的第二十二篇。
今日感受:優化學習方法。

系列目錄:

【復習資料】ES6/ES7/ES8/ES9資料整理(個人整理)

【重溫基礎】1-14篇

【重溫基礎】15.JS對象介紹

【重溫基礎】16.JSON對象介紹

【重溫基礎】17.WebAPI介紹

【重溫基礎】18.相等性判斷

【重溫基礎】19.閉包

【重溫基礎】20.事件

【重溫基礎】21.高階函數

本章節復習的是JS中的內存管理,這對于我們開發非常有幫助。

前置知識
絕大多數的程序語言,他們的內存生命周期基本一致:

分配所需使用的內存 ——(分配內存

使用分配到的內存(讀、寫) ——(使用內存

不需要時將其釋放歸還 ——(釋放內存

對于所有的編程語言,第二部分都是明確的。而第一和第三部分在底層語言中是明確的。
但在像JavaScript這些高級語言中,大部分都是隱含的,因為JavaScript具有自動垃圾回收機制(Garbage collected)。
因此在做JavaScript開發時,不需要關心內存的使用問題,所需內存分配和無用內存回收,都完全實現自動管理。

1.概述
像C語言這樣的高級語言一般都有底層的內存管理接口,比如 malloc()free()。另一方面,JavaScript創建變量(對象,字符串等)時分配內存,并且在不再使用它們時“自動”釋放。 后一個過程稱為垃圾回收。這個“自動”是混亂的根源,并讓JavaScript(和其他高級語言)開發者感覺他們可以不關心內存管理。 這是錯誤的。
——《MDN JavaScript 內存管理》

MDN中的介紹告訴我們,作為JavaScript開發者,還是需要去了解內存管理,雖然JavaScript已經給我們做好自動管理。

2.JavaScript內存生命周期 2.1 分配內存

在做JavaScript開發時,我們定義變量的時候,JavaScript便為我們完成了內存分配:

var num = 100;      // 為數值變量分配內存
var str = "pingan"; // 為字符串變量分配內存
var obj = {
    name : "pingan"
};                  // 為對象變量及其包含的值分配內存
var arr = [1, null, "hi"]; // 為數組變量及其包含的值分配內存

function fun(num){
  return num + 2;
};                  // 為函數(可調用的對象)分配內存

// 函數表達式也能分配一個對象
someElement.addEventListener("click", function(){
  someElement.style.backgroundColor = "blue";
}, false);

另外,通過調用函數,也會分配內存:

// 類型1. 分配對象內存
var date = new Date();   // 分配一個Date對象
var elem = document.createElement("div"); // 分配一個DOM元素

// 類型2. 分配新變量或者新對象
var str1 = "pingan";
var str2 = str1.substr(0, 3); // str2 是一個新的字符串

var arr1 = ["hi", "pingan"];
var arr2 = ["hi", "leo"];
var arr3 = arr1.concat(arr2); // arr3 是一個新的數組(arr1和arr2連接的結果)
2.2 使用內存

使用內存的過程實際上是對分配的內存進行讀取與寫入的操作。
通常表現就是使用定義的值。
讀取與寫入可能是寫入一個變量或者一個對象的屬性值,甚至傳遞函數的參數。

var num = 1;
num ++; // 使用已經定義的變量,做遞增操作
2.3 釋放內存

當我們前面定義好的變量或函數(分配的內存)已經不需要使用的時候,便需要釋放掉這些內存。這也是內存管理中最難的任務,因為我們不知道什么時候這些內存不使用。
很好的是,在高級語言解釋器中,已經嵌入“垃圾回收器”,用來跟蹤內存的分配和使用,以便在內存不使用時自動釋放(這并不是百分百跟蹤到,只是個近似過程)。

3.垃圾回收機制

就像前面提到的,“垃圾回收器”只能解決一般情況,接下來我們需要了解主要的垃圾回收算法和它們局限性。

3.1 引用

垃圾回收算法主要依賴于引用的概念。
即在內存管理環境中,一個對象如果有權限訪問另一個對象,不論顯式還是隱式,稱為一個對象引用另一個對象。
例如:一個JS對象具有對它原型的引用(隱式引用)和對它屬性的引用(顯式引用)。
注意:
這里的對象,不僅包含JS對象,也包含函數作用域(或全局詞法作用域)。

3.2 引用計數垃圾收集

這個算法,把“對象是否不再需要”定義為:當一個對象沒有被其他對象所引用的時候,回收該對象。這是最初級的垃圾收集算法。

var obj = {
    leo : {
        age : 18
    };
};

這里創建2個對象,一個作為leo的屬性被引用,另一個被分配給變量obj

// 省略上面的代碼
/*
我們將前面的
    {
        leo : {
            age : 18
        };
    };
稱為“這個對象”
*/
var obj2 = obj;  // obj2變量是第二個對“這個對象”的引用
obj = "pingan";  // 將“這個對象”的原始是引用obj換成obj2

var leo2 = obj2.leo;  // 引用“這個對象”的leo屬性

可以看出,現在的“這個對象”已經有2個引用,一個是obj2,另一個是leo2

obj2 = "hi"; 
// 將obj2變成零引用,因此,obj2可以被垃圾回收
// 但是它的屬性leo還在被leo2對象引用,所以還不能回收

leo2 = null;
// 將leo變成零引用,這樣obj2和leo2都可以被垃圾回收

這個算法有個限制
無法處理循環引用。即兩個對象創建時相互引用形成一個循環。

function fun(){
    var obj1 = {}, obj2 = {};
    obj1.leo = obj2;  // obj1引用obj2
    obj2.leo = obj1;  // obj2引用obj1
    return "hi pingan";
}
fun();

可以看出,它們被調用之后,會離開函數作用域,已經沒有用了可以被回收,然而引用計數算法考慮到它們之間相互至少引用一次,所以它們不會被回收。

實際案例
在IE6,7中,使用引用計數方式對DOM對象進行垃圾回收,常常造成對象被循環引用導致內存泄露:

var obj;
window.onload = function(){
    obj = document.getElementById("myId");
    obj.leo = obj;
    obj.data = new Array(100000).join("");
};

可以看出,DOM元素obj中的leo屬性引用了自己obj,造成循環引用,若該屬性(leo)沒有移除或設置為null,垃圾回收器總是且至少有一個引用,并一直占用內存,即使從DOM樹刪除,如果這個DOM元素含大量數據(如data屬性)則會導致占用內存永遠無法釋放,出現內存泄露。

3.3 標記清除算法

這個算法,將“對象是否不再需要”定義為:對象是否可以獲得。

標記清除算法,是假定設置一個根對象(root),在JS中是全局對象。垃圾回收器定時找所有從根開始引用的對象,然后再找這些對象引用的對象...直到找到所有可以獲得的對象搜集所有不能獲得的對象

它比引用計數垃圾收集更好,因為“有零引用的對象”總是不可獲得的,但是相反卻不一定,參考“循環引用”。

循環引用不再是問題:

function fun(){
    var obj1 = {}, obj2 = {};
    obj1.leo = obj2;  // obj1引用obj2
    obj2.leo = obj1;  // obj2引用obj1
    return "hi pingan";
}
fun();

還是這個代碼,可以看出,使用標記清除算法來看,函數調用之后,兩個對象無法從全局對象獲取,因此將被回收。相同的,下面案例,一旦 obj 和其事件處理無法從根獲取到,他們將會被垃圾回收器回收。

var obj;
window.onload = function(){
    obj = document.getElementById("myId");
    obj.leo = obj;
    obj.data = new Array(100000).join("");
};

注意: 那些無法從根對象查詢到的對象都將被清除。

3.4 個人小結

在日常開發中,應該注意及時切斷需要回收對象與根的聯系,雖然標記清除算法已經足夠強壯,就像下面代碼:

var obj,ele=document.getElementById("myId");
obj.div = document.createElement("div");
ele.appendChild(obj.div);
// 刪除DOM元素
ele.removeChild(obj.div);

如果我們只是做小型項目開發,JS用的比較少的話,內存管理可以不用太在意,但是如果是大項目(SPA,服務器或桌面應用),那就需要考慮好內存管理問題了。

4.內存泄露(Memory Leak) 4.1 內存泄露概念
在計算機科學中,內存泄漏指由于疏忽或錯誤造成程序未能釋放已經不再使用的內存。內存泄漏并非指內存在物理上的消失,而是應用程序分配某段內存后,由于設計錯誤,導致在釋放該段內存之前就失去了對該段內存的控制,從而造成了內存的浪費。 ——維基百科

其實簡單理解:一些不再使用的內存無法被釋放
當內存占用越來越多,不僅影響系統性能,嚴重的還會導致進程奔潰。

4.2 內存泄露案例

全局變量

未定義的變量,會被定義到全局,當頁面關閉才會銷毀,這樣就造成內存泄露。如下:

function fun(){
    name = "pingan";
};

未銷毀的定時器和回調函數

如果這里舉一個定時器的案例,如果定時器沒有回收,則不僅整個定時器無法被內存回收,定時器函數的依賴也無法回收:

var data = {};
setInterval(function(){
    var render = document.getElementById("myId");
    if(render){
        render.innderHTML = JSON.stringify(data);
    }
}, 1000);

閉包

var str = null;
var fun = function(){
    var str2 = str;
    var unused = function(){
        if(str2) console.log("is unused");
    };
    str = {
        my_str = new Array(100000).join("--");
        my_fun = function(){
            console.log("is my_fun");
        };
    };
};
setInterval(fun, 1000);

定時器中每次調用funstr都會獲得一個包含巨大的數組和一個對于新閉包my_fun的對象,并且unused是一個引用了str2的閉包。
整個案例中,閉包之間共享作用域,盡管unused可能一直沒有調用,但my_fun可能被調用,就會導致內存無法回收,內存增長導致泄露。

DOM引用

當我們把DOM的引用保存在一個數組或Map中,即使移除了元素,但仍然有引用,導致無法回收內存。例如:

var ele = {
    img : document.getElementById("my_img")
};
function fun(){
    ele.img.src = "http://www.baidu.com/1.png";
};
function foo(){
    document.body.removeChild(document.getElementById("my_img"));
};

即使foo方法將my_img元素移除,但fun仍有引用,無法回收。

4.3 內存泄露識別方法

瀏覽器

通過Chrome瀏覽器查看內存占用:

步驟如下:

打開開發者工具,選擇 Timeline 面板

在頂部的Capture字段里面勾選 Memory

點擊左上角的錄制按鈕

在頁面上進行各種操作,模擬用戶的使用情況

一段時間后,點擊對話框的 stop 按鈕,面板上就會顯示這段時間的內存占用情況

如果內存占用基本平穩,接近水平,就說明不存在內存泄漏。

反之,就是內存泄漏了。

命令行

命令行可以使用 Node 提供的process.memoryUsage方法。

console.log(process.memoryUsage());
// { rss: 27709440,
//  heapTotal: 5685248,
//  heapUsed: 3449392,
//  external: 8772 }

process.memoryUsage返回一個對象,包含了 Node 進程的內存占用信息。該對象包含四個字段,單位是字節,含義如下。

rss(resident set size):所有內存占用,包括指令區和堆棧。

heapTotal:"堆"占用的內存,包括用到的和沒用到的。

heapUsed:用到的堆的部分。

external: V8 引擎內部的 C++ 對象占用的內存。

判斷內存泄漏,以heapUsed字段為準。

參考文章

MDN JavaScript指南 內存管理

精讀《JS 中的內存管理》

阮一峰老師JavaScript 內存泄漏教程

本部分內容到這結束

Author 王平安
E-mail pingan8787@qq.com
博 客 www.pingan8787.com
微 信 pingan8787
每日文章推薦 https://github.com/pingan8787...
JS小冊 js.pingan8787.com
微信公眾號 前端自習課

文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。

轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/101725.html

相關文章

  • 重溫JS基礎--變量、作用域和內存問題

    摘要:作用域鏈是保證對執行環境有權訪問的所有變量和函數的有序訪問。如上,包含的作用域鏈包含它自己的變量對象和全局環境的變量對象,為什么能在函數內訪問,這就是通過作用域鏈找的。 前言JavaScript的變量類型是弱類型的,在特定的時間內保存一個特定的值,變量的值和數據類型可以在腳本的生命周期內隨意改變。 1. 基本類型和引用類型的值 JavaScript包含兩種不同類型的值:基本類型和引用類...

    huaixiaoz 評論0 收藏0
  • 重溫基礎】9.正則表達式

    摘要:前置知識中的正則表達式是用來匹配字符串中指定字符組合的模式。另外需要記住正則表達式也是對象。在正則表達式創建時更新,不執行。替換與正則表達式匹配的子串。查找以十六進制數規定的字符。正則表達式拓展介紹在中有兩種情況。 本文是 重溫基礎 系列文章的第九篇。 今日感受:時間管理-角色管理法。 系列目錄: 【復習資料】ES6/ES7/ES8/ES9資料整理(個人整理) 【重溫基礎】1.語...

    ispring 評論0 收藏0
  • 重溫基礎】19.閉包

    摘要:系列目錄復習資料資料整理個人整理重溫基礎篇重溫基礎對象介紹重溫基礎對象介紹重溫基礎介紹重溫基礎相等性判斷本章節復習的是中的關于閉包,這個小哥哥呀,看看。這里隨著閉包函數的結束,執行環境銷毀,變量回收。 本文是 重溫基礎 系列文章的第十九篇。今日感受:將混亂的事情找出之間的聯系,也是種能力。 系列目錄: 【復習資料】ES6/ES7/ES8/ES9資料整理(個人整理) 【重溫基礎】...

    nanfeiyan 評論0 收藏0
  • 重溫基礎】12.使用對象

    摘要:本文是重溫基礎系列文章的第十二篇。注意對象的名稱,對大小寫敏感。基礎用法第一個參數是目標對象,后面參數都是源對象。用途遍歷對象屬性。用途將對象轉為真正的結構。使用場景取出參數對象所有可遍歷屬性,拷貝到當前對象中。類似方法合并兩個對象。 本文是 重溫基礎 系列文章的第十二篇。 今日感受:需要總結下2018。 這幾天,重重的感冒發燒,在家休息好幾天,傷···。 系列目錄: 【復習資料...

    garfileo 評論0 收藏0
  • 重溫JS基礎--引用類型(二)

    摘要:創建一個日期對象中國標準時間在調用構造函數而不傳參數的情況下,新創建的對象自動獲得當前日期和時間。日期格式化方法類型還有一些專門用于將日期格式化為字符串的方法中國標準時間下午以上的這些方法都會根據系統環境而異。 咱們接著上面一篇繼續~ 1. Date類型 JavaScript中的Date類型使用自UTC時間,1970年1月1日零時開始的毫秒數來保存日期。創建一個日期對象: var no...

    NusterCache 評論0 收藏0

發表評論

0條評論

最新活動
閱讀需要支付1元查看
<