這是我參與8月更文挑戰的第6天,活動詳情查看:8月更文挑戰web
限制大小算法
64 位爲 1.4GB,32 位爲 0.7GB瀏覽器
限制緣由緩存
V8 之因此限制了內存的大小,表面上的緣由是 V8 最初是做爲瀏覽器的 JavaScript 引擎而設計,不太可能遇到大量內存的場景,而深層次的緣由則是因爲 V8 的垃圾回收機制的限制。因爲 V8 須要保證 JavaScript 應用邏輯與垃圾回收器所看到的不同,V8 在執行垃圾回收時會阻塞 JavaScript 應用邏輯,直到垃圾回收結束再從新執行 JavaScript 應用邏輯,這種行爲被稱爲「全停頓」(stop-the-world)。若 V8 的堆內存爲 1.5GB,V8 作一次小的垃圾回收須要 50ms 以上,作一次非增量式的垃圾回收甚至要 1 秒以上。這樣瀏覽器將在 1s 內失去對用戶的響應,形成假死現象。若是有動畫效果的話,動畫的展示也將顯著受到影響。markdown
新生代的對象爲存活時間較短的對象,老生代中的對象爲存活時間較長或常駐內存的對象。閉包
V8 引擎的新生代內存大小 32MB(64 位)、16MB(32 位),老生代內存大小爲 1400MB(64 位)、700MB( 32 位)。app
將新生代對象移到老生代dom
晉升條件jsp
新生代區域,採用複製算法, 所以其每時每刻內部都有空閒空間的存在(爲了完成 From 到 To 的對象複製),可是新生代區域空間較小(32M)且被一分爲二,因此這種空間上的浪費也是比較微不足道的。oop
老生代因其空間較大(1.4G),若是一樣採用一分爲二的作法則對空間大小是比較浪費,且老生代空間較大,存放對對象也較多,若是進行復制算法,則其消耗對時間也會更大。也就是是否使用複製算法來進行垃圾回收,是一個時間 T 關於內存大小的關係,當內存大小較小時,使用複製算法消耗的時間是比較短的,而當內存較大時,採用複製算法對時間對消耗也就更大。
增量標記
因爲全停頓會形成了瀏覽器一段時間無響應,因此 V8 使用了一種增量標記的方式,將完整的標記拆分紅不少部分,每作完一部分就停下來,讓 JS 的應用邏輯執行一會,這樣垃圾回收與應用邏輯交替完成。通過增量標記的改進後,垃圾回收的最大停頓時間能夠減小到原來的 1/6 左右
惰性清理
因爲標記完成後,全部的對象都已經被標記,不是死對象就是活對象,堆上多少空間格局已經肯定。咱們能夠沒必要着急釋放那些死對象所佔用的空間,而延遲清理過程的執行。垃圾回收器能夠根據須要逐一清理死對象所佔用的內存空間
其餘
V8 後續還引入了增量式整理(incremental compaction),以及並行標記和並行清理,經過並行利用多核 CPU 來提高垃圾回收的性能
這裏以 Google 瀏覽器爲例,使用 Shift + Esc 喚起 Google 瀏覽器自帶的任務管理器
模擬內存泄漏
在任務管理器裏能夠看到 JavaScript 內存持續上升
document.body.innerHTML = `<button id="add">add</button>`;
document.getElementById('add').addEventListener('click', function (e) {
simulateMemoryLeak();
});
let result = [];
function simulateMemoryLeak() {
setInterval(function () {
result.push(new Array(1000000).join('x'));
document.body.innerHTML = result;
}, 100);
}
複製代碼
這裏以 Google 瀏覽器爲例,使用 F12 開啓調式,選擇 Performance,點擊 record(錄製),進行頁面操做,點擊 stop 結束錄製以後,開啓內存勾選,拖動截圖到指定時間段查看發生內存問題時候到頁面展現,並定位問題。同時能夠查看對應出現紅點到執行腳本,定位問題代碼。
這裏以 Google 瀏覽器爲例,在頁面上進行相關操做後,使用 F12 開啓調式,選擇 Memory,點擊 Take snapshot(拍照),在快照中查找 Detached HTMLElement,回到代碼中查找對應的分離 dom 存在的代碼,在相關操做代碼以後,對分離 dom 進行釋放,防止內存泄漏。
只有頁面的 DOM 樹或 JavaScript 代碼再也不引用 DOM 節點時,DOM 節點纔會被做爲垃圾進行回收。 若是某個節點已從 DOM 樹移除,但某些 JavaScript 仍然引用它,咱們稱此節點爲「已分離」。已分離的 DOM 節點是內存泄漏的常見緣由。
模擬已分離 DOM 節點
document.body.innerHTML = `<button id="add">add</button>`;
document.getElementById('add').addEventListener('click', function (e) {
create();
});
let detachedTree;
function create() {
let ul = document.createElement('ul');
for (let i = 0; i < 10; i++) {
let li = document.createElement('li');
ul.appendChild(li);
}
detachedTree = ul;
}
複製代碼
基於 Benchmark.js
上圖能夠看出,test2 的性能要比 test1 的性能要好,從而得知,全局變量的執行速度,訪問速度要低於局部變量
上圖能夠看出,test2 的性能要比 test1 的性能要好,從而得知,緩存全局變量後使用能夠提高性能
上圖能夠看出,test2 的性能要比 test1 的性能要好,從而得知,經過原型對象添加方法與直接在對象上添加成員方法相比,原型對象上的屬性訪問速度較快。
閉包特色
function foo() {
let name = 'heath';
function fn() {
console.log(name);
}
return fn;
}
let a = foo();
a();
複製代碼
閉包使用不當很容易出現內存泄漏
function f5() {
// el 引用了全局變量document,假設btn節點被刪除後,由於這裏被引用着,因此這裏不會被垃圾回收,致使內存泄漏
let el = document.getElementById('btn');
el.onclick = function (e) {
console.log(e.id);
};
}
f5();
function f6() {
// el 引用了全局變量document,假設btn節點被刪除後,由於這裏被引用着,因此這裏不會被垃圾回收,致使內存泄漏
let el = document.getElementById('btn');
el.onclick = function (e) {
console.log(e.id);
};
el = null; // 咱們這裏手動將el內存釋放,從而當btn節點被刪除後,能夠被垃圾回收
}
f6();
複製代碼
JavaScript 中的面向對象
上圖能夠看出,test2 的性能要比 test1 的性能要好很多,從而得知,直接訪問屬性,會比經過方法訪問屬性速度來的快。
上圖能夠看出,loop 遍歷速度 forEach > 優化 for > for of > for > for in
upload-images.jianshu.io/upload_imag… 上圖能夠看出,節點克隆(cloneNode)生成節點速度要快於建立節點。
上圖能夠看出,字面量聲明的數據生成速度要快於單獨屬性賦值行爲生成的數據。
公衆號:
小何成長
,佛系更文,都是本身曾經踩過的坑或者是學到的東西有興趣的小夥伴歡迎關注我哦,我是:
何小玍。
你們一塊兒進步鴨