摘要:什么是內(nèi)存泄漏簡介,內(nèi)存,硬盤的關系工作的時候需要從存儲器里取數(shù)據(jù)出來。引發(fā)內(nèi)存泄漏的方式意外的全局變量對未聲明變量的處理方式在全局對象上創(chuàng)建該變量的引用即全局對象上的屬性,不是變量,因為它能通過刪除。這樣會造成意外的內(nèi)存泄漏。
什么是內(nèi)存泄漏 簡介 CPU,內(nèi)存,硬盤的關系
CPU(Central Processing Unit)工作的時候:
1、需要從存儲器里取數(shù)據(jù)出來。
2、進行運算,要不停地用存儲器讀寫。
3、計算出結(jié)果再返回到存儲器里。
舉例子形容關系
我們的PC的APP,手機的APP都是跑在內(nèi)存上的。
程序的運行需要內(nèi)存。只要程序提出要求,操作系統(tǒng)就必須供給內(nèi)存。
內(nèi)存就是處于外存和CPU之間的橋梁,用于存儲CPU的運算數(shù)據(jù),這樣內(nèi)存就可以保持記憶功能,你寫的所有代碼,都是需要在內(nèi)存上跑的,虛擬內(nèi)存是從外存上分配的,很慢
內(nèi)存的頻率(mhz)越高代表內(nèi)存運算更快,同一塊內(nèi)存我跑的更快喲,這就是為什么DDR5比DDR3快的原因
說這個的原因,就是如果我的計算機性能足夠好的話,內(nèi)存泄漏帶來的問題就會越來越小。
out of memory
</>復制代碼
內(nèi)存溢出是指程序在申請內(nèi)存時,沒有足夠的內(nèi)存空間供其使用,就會出現(xiàn)內(nèi)存溢出。
在手機上,比如任何一個app,系統(tǒng)初始的時候可能只會給你分配100m的內(nèi)存,如果有android studio的話,可以在log上看到,這個時候你點擊了某個圖片列表頁(為什么用圖片舉例,是因為圖片特有的情況,圖片本身如果是20Kb,長寬為300的話,渲染到手機上由于圖片采用的ARGB-888色彩格式,每個像素點占用4個字節(jié)(雙通道),這樣圖片實際占用內(nèi)存就是3003004/1024/1024 = 300+k dpi為1的情況),這個時候內(nèi)存就會暴漲,一旦接近臨界值,程序就會去找操作系統(tǒng)說,我內(nèi)存不夠了,再給我點,系統(tǒng)就會又給你分配一段,完了你返回首頁了,但是因為你的代碼寫的有問題,暴露各種全局對象啊,各種監(jiān)聽啊,一進一出多次,但是系統(tǒng)給每個app分配的內(nèi)存是有上限的,直到內(nèi)存不夠分,泄漏導致的內(nèi)存溢出。然后crash掉。以前我寫rn的時候,早期的scrollview性能堪憂,出現(xiàn)過內(nèi)存溢出的現(xiàn)象。
內(nèi)存泄漏memory leak
</>復制代碼
內(nèi)存泄漏指的是你申請了一塊內(nèi)存,在使用后無法釋放已申請的內(nèi)存空間,比如程序會認為你可能會用到這個變量,就一直給你留著不釋放,一次內(nèi)存泄漏可以被忽略,但是內(nèi)存泄露堆積后果很嚴重,無論多少內(nèi)存,遲早會被占光。
既然內(nèi)存我可以申請,就可以被系統(tǒng)回收,在C語言中,需要程序員手動malloc去申請內(nèi)存,然后free掉它,這寫起來很麻煩,所以其他大多數(shù)語言都提供了自動回收的機制,那么既然自動回收了,就很容易出現(xiàn)各種問題。
內(nèi)存泄漏的后果通常來說問題并不是特別大,因為正常一個進程的生命周期有限,在當下的大內(nèi)存快cpu的手機下,影響有限,不過還是要列舉一些情況。
1:安卓手機內(nèi)存管理不好,導致只要不重啟,時間越長,可用內(nèi)存越少,即使殺程序。具體緣由可能還和安卓開放過多權限導致無良app各種保持后臺后門運行也有一定關系。
2:導致內(nèi)存溢出,如果手機內(nèi)存被擠占的有限,那么手機會變卡,嚴重的自己crash掉,如果是pc端,瀏覽器的內(nèi)存泄漏導致的溢出會讓瀏覽器出現(xiàn)假死狀態(tài),只能通過強制關閉解決,如果是在webview上,比如我開始的時候?qū)戇^一個代碼在ios微信瀏覽器上調(diào)用swiper 的3d變換導致微信直接閃退。
3:以上還是客戶端的,客戶端大多數(shù)情況下不會停留時間過長,所以除非是非常規(guī)操作,很少會出大問題,但是,跑在服務端的程序,通常都是一直跑幾天甚至是幾個月的,如果這個里面有內(nèi)存泄漏引發(fā)的內(nèi)存溢出的話,那么就會導致服務器宕機,必須重啟。那帶來的損失就很大了。
JavaScript 對未聲明變量的處理方式:在全局對象上創(chuàng)建該變量的引用(即全局對象上的屬性,不是變量,因為它能通過delete刪除)。如果在瀏覽器中,全局對象就是window對象。
如果未聲明的變量緩存大量的數(shù)據(jù),會導致這些數(shù)據(jù)只有在窗口關閉或重新刷新頁面時才能被釋放。這樣會造成意外的內(nèi)存泄漏。
那么為什么會對未聲明的變量處理方式是掛window下呢?
“當引擎執(zhí)行LHS查詢時,如果在頂層(全局作用域)中也無法找到目標變量,全局作用域中就會創(chuàng)建一個具有該名稱的變量,并將其返還給引擎,前提是程序運行在非“嚴格模式”下”
</>復制代碼
摘錄來自: Kyle Simpson、趙望野、梁杰. “你不知道的JavaScript(上卷)。” iBooks.
</>復制代碼
function foo(arg) {
bar = "this is hidden global variable";
}
等同于:
</>復制代碼
function foo(arg) {
window.bar = "this is hidden global variable";
}
另外,通過this創(chuàng)建意外的全局變量:
</>復制代碼
function foo() {
this.variable = "this is hidden global variable";
}
// 當在全局作用域中調(diào)用foo函數(shù),此時this指向的是全局對象(window),而不是"undefined"
foo();
------------->演示
解決方案正常的定義全局變量沒有問題,但是這種是屬于意外的泄漏,所以可以使用嚴格模式處理,規(guī)范自己的代碼。
2.console.log傳遞給console.log的對象是不能被垃圾回收 ??,因為在代碼運行之后需要在開發(fā)工具能查看對象信息。所以最好不要在生產(chǎn)環(huán)境中console.log任何對象。
追蹤線上問題,console絕非是個好的方式。因為發(fā)生問題一般在用戶哪里,你沒辦法看用戶的日志。
</>復制代碼
function aaa() {
this.name = (Array(100000)).join("*");
console.log(this);
}
document.getElementsByClassName("console-obj")[0].addEventListener("click", function () {
var oo = new aaa();
});
------------->演示
解決方案可以刪除自己的console.log,但是顯然,在開發(fā)環(huán)境下,我就是想看我的console.log,這樣注釋來注釋去也挺麻煩的,所以可以判斷下當前的環(huán)境是不是env,如果是product環(huán)境下的話,直接
</>復制代碼
window.console.log = function(){return "warn:do not use my log"}
這樣的手法不僅可以屏蔽console.log,還能防止別人在我們的頁面下console.log調(diào)試
延伸:如何保護自己的頁面安全
3.閉包(closures)由于閉包的特性,通過閉包而能被訪問到的變量,顯然不會被內(nèi)存回收??,因為被回收的話就沒閉包了這個概念了。
</>復制代碼
function foo() {
var str = Array(10000).join("#");
var msg = "test message";
function unused() {
var message = "it is only a test message";
str = "unused: " + str;
}
function getData() {
return msg;
}
return getData;
}
var bar;
document.getElementsByClassName("closure-obj")[0].addEventListener("click", function () {
bar = foo();
});
// var list = [];
// document.getElementsByClassName("closure-obj")[0].addEventListener("click", function () {
// list.push(foo());
// });
演示內(nèi)存performance情況
演示memory 情況
斷點演示閉包scope,call stack
閉包造成的內(nèi)存泄漏占用會比其他的要多。
原因是在相同作用域內(nèi)創(chuàng)建的多個內(nèi)部函數(shù)對象是共享同一個變量對象(variable object)。如果創(chuàng)建的內(nèi)部函數(shù)沒有被其他對象引用,不管內(nèi)部函數(shù)是否引用外部函數(shù)的變量和函數(shù),在外部函數(shù)執(zhí)行完,對應變量對象便會被銷毀。反之,如果內(nèi)部函數(shù)中存在有對外部函數(shù)變量或函數(shù)的訪問(可以不是被引用的內(nèi)部函數(shù)),并且存在某個或多個內(nèi)部函數(shù)被其他對象引用,那么就會形成閉包,外部函數(shù)的變量對象就會存在于閉包函數(shù)的作用域鏈中。這樣確保了閉包函數(shù)有權訪問外部函數(shù)的所有變量和函數(shù)。
延伸:VO/AO,call stack
解決方案不暴露到全局變量上,這樣就不會有問題,暴露到全局變量上就手動置為null,垃圾回收器下次回來會帶走它
4.dom泄漏在 JavaScript 中,DOM 操作是非常耗時的。因為 JavaScript/ECMAScript 引擎獨立于渲染引擎,而 DOM 是位于渲染引擎,相互訪問需要消耗一定的資源。如 Chrome 瀏覽器中 DOM 位于 WebCore,而 JavaScript/ECMAScript 位于 V8 中。假如將 JavaScript/ECMAScript、DOM 分別想象成兩座孤島,兩島之間通過一座收費橋連接,過橋需要交納一定“過橋費”。JavaScript/ECMAScript 每次訪問 DOM 時,都需要交納“過橋費”。因此訪問 DOM 次數(shù)越多,費用越高,頁面性能就會受到很大影響。
為了減少 DOM 訪問次數(shù),一般情況下,當需要多次訪問同一個 DOM 方法或?qū)傩詴r,會將 DOM 引用緩存到一個局部變量中。
但如果在執(zhí)行某些刪除、更新操作后,可能會忘記釋放掉代碼中對應的 DOM 引用,這樣會造成 DOM 內(nèi)存泄露。
</>復制代碼
// 因為要多次用到pre.wrapper、div.container、input.remove、input.add節(jié)點,將其緩存到本地變量中,
var wrapper = document.querySelector(".wrapper");
var container = document.querySelector(".container");
var removeBtn = document.querySelector(".remove");
var addBtn = document.querySelector(".add");
var counter = 0;
var once = true;
// 方法
var hide = function(target){
target.style.display = "none";
}
var show = function(target){
target.style.display = "inline-block";
}
// 回調(diào)函數(shù)
var removeCallback = function(){
removeBtn.removeEventListener("click", removeCallback, false);
addBtn.removeEventListener("click", addCallback, false);
hide(addBtn);
hide(removeBtn);
container.removeChild(wrapper);
wrapper = null;
}
var addCallback = function(){
let p = document.createElement("li");
p.appendChild(document.createTextNode("+ ++counter + ":a new line text
"));
wrapper.appendChild(p);
// 顯示刪除操作按鈕
if(once){
show(removeBtn);
once = false;
}
}
// 綁定事件
removeBtn.addEventListener("click", removeCallback, false);
addBtn.addEventListener("click", addCallback, false);
--------->演示代碼
</>復制代碼
var refA = document.getElementById("refA");
var refB = document.getElementById("refB");
document.body.removeChild(refA);
// #refA不能GC回收,因為存在變量refA對它的引用。將其對#refA引用釋放,但還是無法回收#refA。
refA = null;
// 還存在變量refB對#refA的間接引用(refB引用了#refB,而#refB屬于#refA)。將變量refB對#refB的引用釋放,#refA就可以被GC回收。
refB = null;
5.計時器/監(jiān)聽器
</>復制代碼
var counter = 0;
var clock = {
start: function () {
// setInterval(this.step, 1000);
if(!this.timer){
this.timer = setInterval(this.step, 1000);
}
},
step: function () {
var date = new Date();
var h = date.getHours();
var m = date.getMinutes();
var s = date.getSeconds();
console.log("step running");
}
}
// function goo(){
// // clock = null;
// clearInterval(clock.timer);
// console.log("click stop");
// }
document.querySelector(".start").addEventListener("click", function () {
clock.start();
// document.querySelector(".stop").addEventListener("click",);
});
document.querySelector(".stop").addEventListener("click", function () {
// clock = null;
clearInterval(clock.timer);
});
監(jiān)聽器沒有及時回收或者是匿名回收導致的。
bind,call,apply的區(qū)別
開啟【Performance】項的記錄
執(zhí)行一次 CG,創(chuàng)建基準參考線
操作頁面
執(zhí)行一次 CG
停止記錄
以上就是我們使用的時候的步驟
那么對這個performances里的各項是如何理解的呢?
前置問題1:什么是回流,什么是重繪,以及為什么回流一定會導致重繪,但是重繪不會導致回流?
中置問題2:瀏覽器到了渲染階段的過程是什么?
一次性能的記錄就完整的展示的瀏覽器的渲染全過程。從圖中也可以看出,layout后的階段是Painting
跑一個performances
Performances 各項簡介FPS 每秒的幀數(shù),綠色條約稿,表示FPS值越高,通常上面附帶紅色塊的幀表示該幀時間過長,可能需要優(yōu)化。
CPU CPU資源,面積圖表示不同事件對CPU資源的消耗。
NET 這個項和以前的不一樣,查詢相關資料也沒有找到到底顯示的是什么,所以只能通過下面的具體來看,HTML文件是藍色條,腳本文件是黃色條,樣式文件是紫色條,媒體文件是綠色條,其他的是灰色條,網(wǎng)絡請求部分更詳細的信息建議查看Network。
HEAP 內(nèi)存占用情況
三條虛線:藍色指DOMConentLoaded,綠線表示第一次繪制,紅線表示load事件,很明顯看到load是比較慢的。
summary loading代表html花的時間,scripting代表腳本的時間,rendering代表計算樣式和回流花的時間,painting代表繪制的時間
Bottom-up 代表花費排序
call-tree 代表調(diào)用排序
event-log 代表各項事務時間線
重點看看這個event-log,以回流為例子,再次確認回流后跟著painting,看看有哪些回流,然后去看看時間節(jié)點,發(fā)現(xiàn)對應的頁面出現(xiàn)。
回流操作還是挺占用時間的
以拼團列表圖片高度加載導致的回流問題,可以用一個object-fit來搞定常見的情況
注意代碼規(guī)范,注意代碼規(guī)范,注意代碼規(guī)范
垃圾回收講講垃圾回收,說白了,內(nèi)存泄漏,溢出,就是因為js有自動垃圾回收的機制,然后自動的垃圾回收器并不能準確的回收你所不想用的東西,就會出一些問題,那么常見的垃圾回收有兩種
引用計數(shù)當聲明了一個變量并將一個引用類型值賦給該變量時,則這個值的引用次數(shù)就是 1。 如果同一個值又被賦給另一個變量,則該值的引用次數(shù)加 1。相反,如果包含對這個值引用的變量又取 得了另外一個值,則這個值的引用次數(shù)減 1。當這個值的引用次數(shù)變成 0 時,則說明沒有辦法再訪問這 個值了,因而就可以將其占用的內(nèi)存空間回收回來。這樣,當垃圾收集器下次再運行時,它就會釋放那 些引用次數(shù)為零的值所占用的內(nèi)存。
</>復制代碼
//賦值給o1的對象a{},賦值給o2的對象b{};
var o1 = {
o2: {
x: 1
}
};
//a+1 = 1,b作為屬性也+1 = 1;
var o3 = o1;
//a+1+1 = 2,b+1+1 = 2
o1 = 1;
//a+1+1-1 = 1,b+1+1-1 = 1;
var o4 = o3.o2;
//a+1+1-1 = 1,b+1+1-1+1 = 2;
o3 = "374";
//a+1+1-1-1 = 0,b+1+1-1+1-1 = 1;
o4 = null;
//b-1 = 0;
循環(huán)引用導致的問題
</>復制代碼
//o1:x{},o2:y{};
function f() {
var o1 = {};
//x+1 = 1;
var o2 = {};
//y+1 = 1;
o1.p = o2; // o1 references o2
//y+1+1 = 2;
o2.p = o1; // o2 references o1. This creates a cycle.
//x+1+1 = 2;
}
f();
這段代碼o1和o2互相引用導致引用次數(shù)回收的時候不為1,就沒有辦法回收。
假設沒有o2.p= o1這段,那么o1在出函數(shù)的時候要給對應的對象減一,結(jié)果發(fā)現(xiàn),o1有一個屬性p還沒解除引用,所以先去解o1.p的,這個時候o2的對象就減一次,完了后o1.p就沒了,那o1就可以解除o1的對象,o2再-它自己的,都為0,沒泄漏
反過來,如果上了那段代碼的話,o1要解除,先走p,o1.p想解除,結(jié)果發(fā)現(xiàn)o2有個p,又去解o2.p,死循環(huán),一個都解不了,還是2.
假如這個函數(shù)被重復多次調(diào)用,就會導致大量內(nèi)存得 不到回收。為此,Netscape 在 Navigator 4.0 中放棄了引用計數(shù)方式,轉(zhuǎn)而采用標記清除來實現(xiàn)其垃圾收 集機制。可是,引用計數(shù)導致的麻煩并未就此終結(jié)。到目前為止,幾乎所有的瀏覽器都是使用的標記清楚策略,只不過垃圾收集的時間間隔稍微不同。
標記清除當變量進入環(huán)境(例如,在函數(shù)中聲明一個變量)時,就將這個變量標記為“進入環(huán)境”。從邏輯上講,永遠不能釋放進入環(huán)境的變量所占用的內(nèi)存,因為只要執(zhí)行流進入相應的環(huán)境,就可能會用到它們。而當變量離開環(huán)境時,則將其 標記為“離開環(huán)境”。
Mark and sweep過去幾年,JavaScript 垃圾回收(代數(shù)、增量、并行、并行垃圾收集)領域的所有改進都是對該算法(mark-and-sweep)的實現(xiàn)進行改進,但并沒有對垃圾回收算法本身進行改進,其目標是確定一個對象是否可達。
這樣的話,循環(huán)引用將不再是問題
盡管兩個對象還是存在引用,但是他們從 root 出發(fā)已經(jīng)是不可達的了。
在Javascript中,徹底避免垃圾回收或者是內(nèi)存泄漏是非常困難的。所以我們能做的就是減少泄漏,減少垃圾回收的頻率。對一些高頻使用的函數(shù)之類的東西去做一些類似的優(yōu)化。綜合考慮優(yōu)化成本
文章版權歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://specialneedsforspecialkids.com/yun/95025.html
摘要:的內(nèi)存泄漏對于這門語言的使用者來說,大多數(shù)的使用者的內(nèi)存管理意識都不強。內(nèi)存泄漏的定義指由于疏忽或錯誤造成程序未能釋放已經(jīng)不再使用的內(nèi)存的情況。 javascript的內(nèi)存泄漏 對于JavaScript這門語言的使用者來說,大多數(shù)的使用者的內(nèi)存管理意識都不強。因為JavaScript一直以來都只作為在網(wǎng)頁上使用的腳本語言,而網(wǎng)頁往往都不會長時間的運行,所以使用者對JavaScript的...
摘要:內(nèi)存泄漏與內(nèi)存溢出內(nèi)存溢出指的是程序向系統(tǒng)申請一定大小內(nèi)存,而系統(tǒng)不能滿足程序的要求就是內(nèi)存的溢出。內(nèi)存泄漏指申請的內(nèi)存一直得不到釋放,回收不了。真正有危害的是內(nèi)存泄漏的堆積,這會最終消耗盡系統(tǒng)所有的內(nèi)存。 內(nèi)存泄漏與內(nèi)存溢出 1.內(nèi)存溢出:指的是程序向系統(tǒng)申請一定大小內(nèi)存,而系統(tǒng)不能滿足程序的要求就是內(nèi)存的溢出。2.內(nèi)存泄漏:指申請的內(nèi)存一直得不到釋放,GC回收不了。一般在項目中就是...
摘要:所謂的內(nèi)存泄漏簡單來說是不再用到的內(nèi)存,沒有及時釋放。如果一個值不再需要了,引用數(shù)卻不為,垃圾回收機制無法釋放這塊內(nèi)存,從而導致內(nèi)存泄漏。 前言 程序的運行需要內(nèi)存。只要程序提出要求,操作系統(tǒng)或者運行時就必須供給內(nèi)存。所謂的內(nèi)存泄漏簡單來說是不再用到的內(nèi)存,沒有及時釋放。為了更好避免內(nèi)存泄漏,我們先介紹Javascript垃圾回收機制。 在C與C++等語言中,開發(fā)人員可以直接控制內(nèi)存的...
摘要:所謂的內(nèi)存泄漏簡單來說是不再用到的內(nèi)存,沒有及時釋放。如果一個值不再需要了,引用數(shù)卻不為,垃圾回收機制無法釋放這塊內(nèi)存,從而導致內(nèi)存泄漏。 前言 程序的運行需要內(nèi)存。只要程序提出要求,操作系統(tǒng)或者運行時就必須供給內(nèi)存。所謂的內(nèi)存泄漏簡單來說是不再用到的內(nèi)存,沒有及時釋放。為了更好避免內(nèi)存泄漏,我們先介紹Javascript垃圾回收機制。 在C與C++等語言中,開發(fā)人員可以直接控制內(nèi)存的...
摘要:接上回我寫了一篇關于閉包的博客學習之閉包,最后談到閉包導致的問題時留了一個尾在以下的瀏覽器中會有內(nèi)存泄漏的問題。今天的博客就繼續(xù)探索一下內(nèi)存泄漏的問題。博客地址的前端之路原文鏈接學習之內(nèi)存泄漏 接上回我寫了一篇關于閉包的博客《學習JavaScript之閉包》, 最后談到閉包導致的問題時留了一個尾: 在IE9以下的瀏覽器中會有內(nèi)存泄漏的問題。 今天的博客就繼續(xù)探索一下內(nèi)存泄漏的問題。 淺...
閱讀 1340·2021-11-15 11:37
閱讀 2219·2021-09-23 11:21
閱讀 1306·2019-08-30 15:55
閱讀 2111·2019-08-30 15:55
閱讀 2821·2019-08-30 15:52
閱讀 2825·2019-08-30 11:12
閱讀 1580·2019-08-29 18:45
閱讀 1894·2019-08-29 14:04