欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

深入理解JavaScript內(nèi)存管理和GC算法

 更新時(shí)間:2022年07月13日 09:39:56   作者:??一碗周???  
這篇文章主要介紹了深入理解JavaScript內(nèi)存管理和GC算法,下面文章主要講解JavaScript的垃圾回收機(jī)制以及常用的垃圾回收算法;還講解了V8引擎中的內(nèi)存管理,最后介紹了Performance工具如何使用

前言

JavaScript在創(chuàng)建變量(數(shù)組、字符串、對(duì)象等)是自動(dòng)進(jìn)行了分配內(nèi)存,并且在不使用它們的時(shí)候會(huì)“自動(dòng)”的釋放分配的內(nèi)容;JavaScript語(yǔ)言不像其他底層語(yǔ)言一樣,例如C語(yǔ)言,他們提供了內(nèi)存管理的接口,比如malloc()用于分配所需的內(nèi)存空間、free()釋放之前所分配的內(nèi)存空間。

我們將釋放內(nèi)存的過(guò)程稱(chēng)為垃圾回收,像JavaScript這種高級(jí)語(yǔ)言提供了內(nèi)存自動(dòng)分配和自動(dòng)回收,因?yàn)檫@個(gè)自動(dòng)就導(dǎo)致許多開(kāi)發(fā)者不會(huì)去關(guān)心內(nèi)存管理。

即使高級(jí)語(yǔ)言提供了自動(dòng)內(nèi)存管理,但是我們也需要對(duì)內(nèi)管管理有一下基本的理解,有時(shí)候自動(dòng)內(nèi)存管理出現(xiàn)了問(wèn)題,我們可以更好的去解決它,或者說(shuō)使用代價(jià)最小的方法解決它。

內(nèi)存的生命周期

其實(shí)不管是什么語(yǔ)言,內(nèi)存的聲明周期大致分為如下幾個(gè)階段:

下面我們對(duì)每一步進(jìn)行具體說(shuō)明:

  • 內(nèi)存分配:當(dāng)我們定義變量時(shí),系統(tǒng)會(huì)自動(dòng)為其分配內(nèi)存,它允許在程序中使用這塊內(nèi)存。
  • 內(nèi)存使用:在對(duì)變量進(jìn)行讀寫(xiě)的時(shí)候發(fā)生
  • 內(nèi)存回收:使用完畢后,自動(dòng)釋放不需要內(nèi)存,也就是由垃圾回收機(jī)制自動(dòng)回收不再使用的內(nèi)存

JavaScript中的內(nèi)存分配

為了保護(hù)開(kāi)發(fā)人員的頭發(fā),JavaScript在定義變量時(shí)就自動(dòng)完成了內(nèi)存分配,示例代碼如下:

let num = 123 // 給數(shù)值變量分配內(nèi)存
let str = '一碗周' // 給字符串分配內(nèi)存

let obj = {
  name: '一碗周',
  age: 18,
} // 給對(duì)象及其包含的值分配內(nèi)存

// 給數(shù)組及其包含的值分配內(nèi)存(類(lèi)似于對(duì)象)
let arr = [1, null, 'abc']

function fun(a) {
  return a + 2
} // 給函數(shù)(可調(diào)用的對(duì)象)分配內(nèi)存

// 函數(shù)表達(dá)式也能分配一個(gè)對(duì)象
Element.addEventListener(
  'click',
  event => {
    console.log(event)
  },
  false,
)

有些時(shí)候并不會(huì)重新分配內(nèi)存,如下面這段代碼:

// 給數(shù)組及其包含的值分配內(nèi)存(類(lèi)似于對(duì)象)
let arr = [1, null, 'abc']

let arr2 = [arr[0], arr[2]]
// 這里并不會(huì)重新對(duì)分配內(nèi)存,而是直接存儲(chǔ)原來(lái)的那份內(nèi)存

在JavaScript中使用內(nèi)存

JavaScript中使用值的過(guò)程實(shí)際上是對(duì)分配內(nèi)存進(jìn)行讀取與寫(xiě)入的操作。這里的讀取與寫(xiě)入可能是寫(xiě)入一個(gè)變量、讀取某個(gè)變量的值、寫(xiě)入一個(gè)對(duì)象的屬性值以及為函數(shù)傳遞參數(shù)等。

釋放內(nèi)存

JavaScript中的內(nèi)存釋放是自動(dòng)的,釋放的時(shí)機(jī)就是某些值(內(nèi)存地址)不在使用了,JavaScript就會(huì)自動(dòng)釋放其占用的內(nèi)存。

其實(shí)大多數(shù)內(nèi)存管理的問(wèn)題都在這個(gè)階段。在這里最艱難的任務(wù)就是找到那些不需要的變量。

雖然說(shuō)現(xiàn)在打高級(jí)語(yǔ)言都有自己垃圾回收機(jī)制,雖然現(xiàn)在的垃圾回收算法很多,但是也無(wú)法智能的回收所有的極端情況,這就是我們?yōu)槭裁匆獙W(xué)習(xí)內(nèi)存管理以及垃圾回收算法的理由了。

接下來(lái)我們來(lái)討論一下JavaScript中的垃圾回收以及常用的垃圾回收算法。

JavaScript中的垃圾回收

前面我們也說(shuō)了,JavaScript中的內(nèi)存管理是自動(dòng)的,在創(chuàng)建對(duì)象時(shí)會(huì)自動(dòng)分配內(nèi)存,當(dāng)對(duì)象不在被引用或者不能從根上訪問(wèn)時(shí),就會(huì)被當(dāng)做垃圾給回收掉。

JavaScript中的可達(dá)對(duì)象簡(jiǎn)單的說(shuō)就是可以訪問(wèn)到的對(duì)象,不管是通過(guò)引用還是作用域鏈的方式,只要能訪問(wèn)到的就稱(chēng)之為可達(dá)對(duì)象??蛇_(dá)對(duì)象的可達(dá)是有一個(gè)標(biāo)準(zhǔn)的,就是必須從根上出發(fā)是否能被找到;這里的根可以理解為JavaScript中的全局變量對(duì)象,在瀏覽器環(huán)境中就是window、在Node環(huán)境中就是global。

為了更好的理解引用的概念,看下面這一段代碼:

let person = {
  name: '一碗周',
}
let man = person
person = null

圖解如下:

根據(jù)上面那個(gè)圖我們可以看到,最終這個(gè){ name: '一碗周' }是不會(huì)被當(dāng)做垃圾給回收掉的,因?yàn)檫€具有一個(gè)引用。

現(xiàn)在我們來(lái)理解一下可達(dá)對(duì)象,代碼如下:

function groupObj(obj1, obj2) {
  obj1.next = obj2
  obj2.prev = obj1

  return {
    obj1,
    obj2,
  }
}
let obj = groupObj({ name: '大明' }, { name: '小明' })

調(diào)用groupObj()函數(shù)的的結(jié)果obj是一個(gè)包含兩個(gè)對(duì)象的一個(gè)對(duì)象,其中obj.obj1next屬性指向obj.obj2;而obj.obj2prev屬性又指向obj.obj2。最終形成了一個(gè)無(wú)限套娃。

如下圖:

現(xiàn)在來(lái)看下面這段代碼:

delete obj.obj1
delete obj.obj2.prev

我們刪除obj對(duì)象中的obj1對(duì)象的引用和obj.obj2中的prev屬性對(duì)obj1的引用。

圖解如下:

此時(shí)的obj1就被當(dāng)做垃圾給回收了。

GC算法

GC是Garbage collection的簡(jiǎn)寫(xiě),也就是垃圾回收。當(dāng)GC進(jìn)行工作的時(shí)候,它可以找到內(nèi)存中的垃圾、并釋放和回收空間,回收之后方便我們后續(xù)的進(jìn)行使用。

在GC中的垃圾包括程序中不在需要使用的對(duì)象以及程序中不能再訪問(wèn)到的對(duì)象都會(huì)被當(dāng)做垃圾。

GC算法就是工作時(shí)查找和回收所遵循的規(guī)則,常見(jiàn)的GC算法有如下幾種:

  • 引用計(jì)數(shù):通過(guò)一個(gè)數(shù)字來(lái)記錄引用次數(shù),通過(guò)判斷當(dāng)前數(shù)字是不是0來(lái)判斷對(duì)象是不是一個(gè)垃圾。
  • 標(biāo)記清除:在工作時(shí)為對(duì)象添加一個(gè)標(biāo)記來(lái)判斷是不是垃圾。
  • 標(biāo)記整理:與標(biāo)記清除類(lèi)似。
  • 分代回收:V8中使用的垃圾回收機(jī)制。

引用計(jì)數(shù)算法

引用計(jì)數(shù)算法的核心思想就是設(shè)置一個(gè)引用計(jì)數(shù)器,判斷當(dāng)前引用數(shù)是否為0 ,從而決定當(dāng)前對(duì)象是不是一個(gè)垃圾,從而垃圾回收機(jī)制開(kāi)始工作,釋放這塊內(nèi)存。

引用計(jì)數(shù)算法的核心就是引用計(jì)數(shù)器 ,由于引用計(jì)數(shù)器的存在,也就導(dǎo)致該算法與其他GC算法有所差別。

引用計(jì)數(shù)器的改變是在引用關(guān)系發(fā)生改變時(shí)就會(huì)發(fā)生變化,當(dāng)引用計(jì)數(shù)器變?yōu)?的時(shí)候,該對(duì)象就會(huì)被當(dāng)做垃圾回收。

現(xiàn)在我們通過(guò)一段代碼來(lái)看一下:

// { name: '一碗周' } 的引用計(jì)數(shù)器 + 1
let person = {
  name: '一碗周',
}
// 又增加了一個(gè)引用,引用計(jì)數(shù)器 + 1
let man = person
// 取消一個(gè)引用,引用計(jì)數(shù)器 - 1
person = null
// 取消一個(gè)引用,引用計(jì)數(shù)器 - 1。此時(shí) { name: '一碗周' } 的內(nèi)存就會(huì)被當(dāng)做垃圾回收
man = null

引用計(jì)數(shù)算法的優(yōu)點(diǎn)如下:

  • 發(fā)現(xiàn)垃圾時(shí)立即回收;
  • 最大限度減少程序暫停,這里因?yàn)榘l(fā)現(xiàn)垃圾就立刻回收了,減少了程序因內(nèi)存爆滿(mǎn)而被迫停止的現(xiàn)象。

缺點(diǎn)如下:

  • 無(wú)法回收循環(huán)引用的對(duì)象;

就比如下面這段代碼:

function fun() {
  const obj1 = {}
  const obj2 = {}
  obj1.next = obj2
  obj2.prev = obj1
  return '一碗周'
}
fun()

上面的代碼中,當(dāng)函數(shù)執(zhí)行完成之后函數(shù)體的內(nèi)容已經(jīng)是沒(méi)有作用的了,但是由于obj1obj2都存在不止1個(gè)引用,導(dǎo)致兩種都無(wú)法被回收,就造成了空間內(nèi)存的浪費(fèi)。

  • 時(shí)間開(kāi)銷(xiāo)大,這是因?yàn)橐糜?jì)數(shù)算法需要時(shí)刻的去監(jiān)控引用計(jì)數(shù)器的變化。

標(biāo)記清除算法

標(biāo)記清除算法解決了引用計(jì)數(shù)算法的?些問(wèn)題, 并且實(shí)現(xiàn)較為簡(jiǎn)單, 在V8引擎中會(huì)有被?量的使?到。

在使?標(biāo)記清除算法時(shí),未引用對(duì)象并不會(huì)被立即回收.取?代之的做法是,垃圾對(duì)象將?直累計(jì)到內(nèi)存耗盡為?.當(dāng)內(nèi)存耗盡時(shí),程序?qū)?huì)被掛起,垃圾回收開(kāi)始執(zhí)行.當(dāng)所有的未引用對(duì)象被清理完畢 時(shí),程序才會(huì)繼續(xù)執(zhí)行.該算法的核心思想就是將整個(gè)垃圾回收操作分為標(biāo)記和清除兩個(gè)階段完成。

第一個(gè)階段就是遍歷所有對(duì)象,標(biāo)記所有的可達(dá)對(duì)象;第二個(gè)階段就是遍歷所有對(duì)象清除沒(méi)有標(biāo)記的對(duì)象,同時(shí)會(huì)抹掉所有已經(jīng)標(biāo)記的對(duì)象,便于下次的工作。

為了區(qū)分可用對(duì)象與垃圾對(duì)象,我們需要在每?個(gè)對(duì)象中記錄對(duì)象的狀態(tài)。 因此, 我們?cè)诿?個(gè)對(duì)象中加?了?個(gè)特殊的布爾類(lèi)型的域, 叫做marked。 默認(rèn)情況下, 對(duì)象被創(chuàng)建時(shí)處于未標(biāo)記狀態(tài)。 所以, marked 域被初始化為false。

標(biāo)記清除算法的圖解如下圖所示:

進(jìn)行垃圾回收完畢之后,將回收的內(nèi)存放在空閑鏈表中方便我們后續(xù)使用。

標(biāo)記清除算法最大的優(yōu)點(diǎn)就是解決了引用計(jì)數(shù)算法無(wú)法回收循環(huán)引用的對(duì)象的問(wèn)題 。就比如下面這段代碼:

function fun() {
  const obj1 = {},
    obj2 = {},
    obj3 = {},
    obj4 = {},
    obj5 = {},
    obj6 = {}
  obj1.next = obj2

  obj2.next = obj3
  obj2.prev = obj6

  obj4.next = obj6
  obj4.prev = obj1

  obj5.next = obj4
  obj5.prev = obj6
  return obj1
}
const obj = fun()

當(dāng)函數(shù)執(zhí)行完畢后obj4的引用并不是0,但是使用引用計(jì)數(shù)算法并不能將其作為垃圾回收掉,而使用標(biāo)記清除算法就解決了這個(gè)問(wèn)題。

而標(biāo)記清除算法的缺點(diǎn)也是有的,這種算法會(huì)導(dǎo)致內(nèi)存碎片化,地址不連續(xù);還有就是使用標(biāo)記清除算法即使發(fā)現(xiàn)了垃圾對(duì)象不里能立刻清除,需要到第二次去清除。

標(biāo)記整理算法

標(biāo)記整理算法可以看做是標(biāo)記清除算法的增強(qiáng)型,其步驟也是分為標(biāo)記和清除兩個(gè)階段。

但是標(biāo)記整理算法那的清除階段會(huì)先進(jìn)行整理,移動(dòng)對(duì)象的位置,最后進(jìn)行清除。

步驟如下圖:

V8中的內(nèi)存管理

V8是什么

V8是一款主流的JavaScript執(zhí)行引擎,現(xiàn)在的Node.js和大多數(shù)瀏覽器都采用V8作為JavaScript的引擎。V8的編譯功能采用的是及時(shí)編譯,也稱(chēng)為動(dòng)態(tài)翻譯或運(yùn)行時(shí)編譯,是一種執(zhí)行計(jì)算機(jī)代碼的方法,這種方法涉及在程序執(zhí)行過(guò)程中(在執(zhí)行期)而不是在執(zhí)行之前進(jìn)行編譯。

V8引擎對(duì)內(nèi)存是設(shè)有上限的,在64位操作系統(tǒng)下上限是1.5G的,而32位操作系統(tǒng)的上限是800兆的。至于為什么設(shè)置內(nèi)存上限主要是內(nèi)容V8引擎主要是為瀏覽器而準(zhǔn)備的,不適合太大的空間;還有一點(diǎn)就是這個(gè)大小的垃圾回收是很快的,用戶(hù)幾乎沒(méi)有感覺(jué),從而增加用戶(hù)體驗(yàn)。

V8垃圾回收策略

V8引擎采用的是分代回收的思想,主要是將我們的內(nèi)存按照一定的規(guī)則分成兩類(lèi),一個(gè)是新生代存儲(chǔ)區(qū),另一個(gè)是老生代存儲(chǔ)區(qū)。

新生代的對(duì)象為存活時(shí)間較短的對(duì)象,簡(jiǎn)單來(lái)說(shuō)就是新產(chǎn)生的對(duì)象,通常只支持一定的容量(64位操作系統(tǒng)32兆、32位操作系統(tǒng)16兆),而老生代的對(duì)象為存活事件較長(zhǎng)或常駐內(nèi)存的對(duì)象,簡(jiǎn)單來(lái)說(shuō)就是經(jīng)歷過(guò)新生代垃圾回收后還存活下來(lái)的對(duì)象,容量通常比較大。

下圖展示了V8中的內(nèi)存:

V8引擎會(huì)根據(jù)不同的對(duì)象采用不同的GC算法,V8中常用的GC算法如下:

  • 分代回收
  • 空間復(fù)制
  • 標(biāo)記清除
  • 標(biāo)記整理
  • 標(biāo)記增量

新生代對(duì)象垃圾回收

上面我們也介紹了,新生代中存放的是存活時(shí)間較短的對(duì)象。新生代對(duì)象回收過(guò)程采用的是復(fù)制算法和標(biāo)記整理算法。

復(fù)制算法將我們的新生代內(nèi)存區(qū)域劃分為兩個(gè)相同大小的空間,我們將當(dāng)前使用狀態(tài)的空間稱(chēng)之為From狀態(tài),空間狀態(tài)的空間稱(chēng)之為T(mén)o狀態(tài),

如下圖所示:

我們將活動(dòng)的對(duì)象全部存儲(chǔ)到From空間,當(dāng)空間接近滿(mǎn)的時(shí)候,就會(huì)觸發(fā)垃圾回收。

首先需要將新生代From空間中的活動(dòng)對(duì)象做標(biāo)記整理,標(biāo)記整理完成之后將標(biāo)記后的活動(dòng)對(duì)象拷貝到To空間并將沒(méi)有進(jìn)行標(biāo)記的對(duì)象進(jìn)行回收;最后將From空間和To空間進(jìn)行交換。

還有一點(diǎn)需要說(shuō)的就是在進(jìn)行對(duì)象拷貝的時(shí)候,會(huì)出現(xiàn)新生代對(duì)象移動(dòng)至老生代對(duì)象中。

這些被移動(dòng)的對(duì)象是具有指定條件的,主要有兩種:

  • 經(jīng)過(guò)一輪垃圾回收還存活的新生代對(duì)象會(huì)被移動(dòng)到老生代對(duì)象中
  • 在To空間占用率超過(guò)了25%,這個(gè)對(duì)象也會(huì)被移動(dòng)到老生代對(duì)象中(25%的原因是怕影響后續(xù)內(nèi)存分配)

如此可知,新生代對(duì)象的垃圾回收采用的方案是空間換時(shí)間。

老生代對(duì)象垃圾回收

老生代區(qū)域存放的對(duì)象就是存活時(shí)間長(zhǎng)、占用空間大的對(duì)象。也正是因?yàn)槠浯婊畹臅r(shí)間長(zhǎng)且占用空間大,也就導(dǎo)致了不能采用復(fù)制算法,如果采用復(fù)制算法那就會(huì)造成時(shí)間長(zhǎng)和空間浪費(fèi)的現(xiàn)象。

老生代對(duì)象一般采用標(biāo)記清除、標(biāo)記整理和增量標(biāo)記算法進(jìn)行垃圾回收。

在清除階段主要才采用標(biāo)記清除算法來(lái)進(jìn)行回收,當(dāng)一段時(shí)候后,就會(huì)產(chǎn)生大量不連續(xù)的內(nèi)存碎片,過(guò)多的碎片無(wú)法分配足夠的內(nèi)存時(shí),就會(huì)采用標(biāo)記整理算法來(lái)整理我們的空間碎片。

老生代對(duì)象的垃圾回收會(huì)采用增量標(biāo)記算法來(lái)優(yōu)化垃圾回收的過(guò)程,增量標(biāo)記算法如下圖所示:

由于JavaScript是單線程,所以程序執(zhí)行和垃圾回收同時(shí)只能運(yùn)行一個(gè),這就會(huì)導(dǎo)致在執(zhí)行垃圾回收的時(shí)候程序卡頓,這樣給用戶(hù)的體驗(yàn)肯定是不好的。

所以提出了增量標(biāo)記,在程序運(yùn)行時(shí),程序先跑一段時(shí)間,然后進(jìn)行進(jìn)行初步的標(biāo)記,這個(gè)標(biāo)記有可能只標(biāo)記直接可達(dá)的對(duì)象,然后程序繼續(xù)跑一段時(shí)間,在進(jìn)行增量標(biāo)記 ,也即是標(biāo)記哪些間接可達(dá)的對(duì)象。由此反復(fù),直至結(jié)束。

Performance工具

由于JavaScript沒(méi)有給我們提供操作內(nèi)存的API,只能靠本身提供的內(nèi)存管理,但是我們并不知道實(shí)際上的內(nèi)存管理是什么樣的。而有時(shí)我們需要時(shí)刻關(guān)注內(nèi)存的使用情況,Performance工具提供了多種監(jiān)控內(nèi)存的方式。

Performance使用步驟

首先我們打開(kāi)Chrome瀏覽器(這里我們使用的是Chrome瀏覽器,其他瀏覽器也是可以的),在地址欄中輸入我們的目標(biāo)地址,然后打開(kāi)開(kāi)發(fā)者工具,選擇【性能】面板。

選擇性能面板后開(kāi)啟錄制功能,然后去訪問(wèn)具體界面,模仿用戶(hù)去執(zhí)行一些操作,然后停止錄制,最后我們可以在分析界面中分析記錄的內(nèi)存信息。

結(jié)果如下圖所示:

內(nèi)存問(wèn)題的體現(xiàn)

出現(xiàn)內(nèi)存的問(wèn)題主要有如下幾種表現(xiàn):

  • 頁(yè)面出現(xiàn)延遲加載或經(jīng)常性暫停,它的底層就伴隨著頻繁的垃圾回收的執(zhí)行;為什么會(huì)頻繁的進(jìn)行垃圾回收,可能是一些代碼直接導(dǎo)致內(nèi)存爆滿(mǎn)而且需要立刻進(jìn)行垃圾回收。

關(guān)于這個(gè)問(wèn)題我們可以通過(guò)內(nèi)存變化圖進(jìn)行分析其原因:

  • 頁(yè)面持續(xù)性出現(xiàn)糟糕的性能表現(xiàn),也就是說(shuō)在我們使用的過(guò)程中,頁(yè)面給我們的感覺(jué)就是一直不好用,它的底層我們一般認(rèn)為都會(huì)存在內(nèi)存膨脹,所謂的內(nèi)存膨脹就是當(dāng)前頁(yè)面為了達(dá)到某種速度從而申請(qǐng)遠(yuǎn)大于本身需要的內(nèi)存,申請(qǐng)的這個(gè)存在超過(guò)了我們?cè)O(shè)備本身所能提供的大小,這個(gè)時(shí)候我們就能感知到一個(gè)持續(xù)性的糟糕性能的體驗(yàn)。

導(dǎo)致內(nèi)存膨脹的問(wèn)題有可能是我們代碼的問(wèn)題,也有可能是設(shè)備本身就很差勁,想要分析定位并解決的話(huà)需要我們?cè)诙鄠€(gè)設(shè)備上進(jìn)行反復(fù)的測(cè)試

  • 頁(yè)面的性能隨著時(shí)間的延長(zhǎng)導(dǎo)致頁(yè)面越來(lái)越差,加載時(shí)間越來(lái)越長(zhǎng),出現(xiàn)這種問(wèn)題的原因可能是由于代碼的原因出現(xiàn)內(nèi)存泄露。

想要檢測(cè)內(nèi)存是否泄漏,我們可以通過(guò)內(nèi)存總視圖來(lái)監(jiān)聽(tīng)我們的內(nèi)存,如果內(nèi)存是持續(xù)升高的,就可能已經(jīng)出現(xiàn)了內(nèi)存泄露。

監(jiān)控內(nèi)存的方式

在瀏覽器中監(jiān)控內(nèi)存主要有以下幾種方式:

  • 瀏覽器提供的任務(wù)管理器
  • Timeline時(shí)序圖
  • 堆快照查找分離DOM
  • 判斷是否存在頻繁的垃圾回收

接下來(lái)我們就分別講解這幾種方式。

任務(wù)管理器監(jiān)控內(nèi)存

在瀏覽器中按【Shift】+【ESC】鍵即可打開(kāi)瀏覽器提供的任務(wù)管理器,下圖展示了Chrome瀏覽器中的任務(wù)管理器,我們來(lái)解讀一下

上圖中我們可以看到【掘金】標(biāo)簽頁(yè)的【內(nèi)存占用空間】表示的的這個(gè)頁(yè)面的DOM在瀏覽器中所占的內(nèi)存,如果不斷增加就表示有新的DOM在創(chuàng)建;而后面的【JavaScript使用的內(nèi)存】(默認(rèn)不開(kāi)啟,需要通過(guò)右鍵開(kāi)啟)表示的是JavaScript中的堆,而括號(hào)中的大小表示JavaScript中的所有可達(dá)對(duì)象。

Timeline記錄內(nèi)存

上面描述的瀏覽器中提供的任務(wù)管理器只能用來(lái)幫助我們判斷頁(yè)面是否存在問(wèn)題,卻不能定位頁(yè)面的問(wèn)題。

Timeline是Performance工具中的一個(gè)小的選項(xiàng)卡,其中以毫秒為單位記錄了頁(yè)面中的情況,從而可以幫助我們更簡(jiǎn)單的定位問(wèn)題。

堆快照查找分離DOM

堆快照是很有針對(duì)性的查找當(dāng)前的界面對(duì)象中是否存在一些分離的DOM,分離DOM的存在也就是存在內(nèi)存泄漏。

首先我們先要弄清楚DOM有幾種狀態(tài):

  • 首先,DOM對(duì)象存在DOM樹(shù)中,這屬于正常的DOM
  • 然后,不存在DOM樹(shù)中且不存在JS引用,這屬于垃圾DOM對(duì)象,是需要被回收的
  • 最后,不存在DOM樹(shù)中但是存在JS引用,這就是分離DOM,需要我們手動(dòng)進(jìn)行釋放。

查找分離DOM的步驟:打開(kāi)開(kāi)發(fā)者工具→【內(nèi)存面板】→【用戶(hù)配置】→【獲取快照】→在【過(guò)濾器】中輸入Detached來(lái)查找分離DOM,

如下圖所示:

查找到創(chuàng)建的分離DOM后,我們找到該DOM的引用,然后進(jìn)行釋放。

判斷是否存在頻繁的垃圾回收

因?yàn)?code>GC工作時(shí)應(yīng)用程序是停止的,如果當(dāng)前垃圾回收頻繁工作,而且時(shí)間過(guò)長(zhǎng)的話(huà)對(duì)頁(yè)面來(lái)說(shuō)很不友好,會(huì)導(dǎo)致應(yīng)用假死說(shuō)我狀態(tài),用戶(hù)使用中會(huì)感知應(yīng)用有卡頓。

我們可以通過(guò)如下方式進(jìn)行判斷是否存在頻繁的垃圾回收,具體如下:

  • 通過(guò)Timeline時(shí)序圖判斷,對(duì)當(dāng)前性能面板中的內(nèi)存走勢(shì)進(jìn)行監(jiān)控,如果其中頻繁的上升下降,就出現(xiàn)了頻繁的垃圾回收。這個(gè)時(shí)候要定位代碼,看看是執(zhí)行什么的時(shí)候造成了這種情況。
  • 使用瀏覽器任務(wù)管理器會(huì)簡(jiǎn)單一些,任務(wù)管理器中主要是數(shù)值的變化,其數(shù)據(jù)頻繁的瞬間增加減小,也是頻繁的垃圾回收。

寫(xiě)在最后

本篇文章介紹了JavaScript的垃圾回收機(jī)制以及常用的垃圾回收算法;還講解了V8引擎中的內(nèi)存管理,最后介紹了Performance工具如何使用。

到此這篇關(guān)于深入理解JavaScript內(nèi)存管理和GC算法的文章就介紹到這了,更多相關(guān)JS內(nèi)存管理內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

最新評(píng)論