javaScript 内存管理机制
大家好,今天分享的主題為 JavaScript 內存管理機制,本次分享將從以下三部分進行講述:
-
js 內存管理與 js 垃圾
-
常見的 GC 算法
-
V8 引擎的垃圾回收
js 內存管理與 js 垃圾
關于 JavaScript 內存管理機制,相信大家都有所了解。我們就簡單看一下 js 內存管理與 js 垃圾。JavaScript 內存管理是由 JS 自動操作的,不需要人為進行參與,這些內存管理包含以下三項:
-
申請內存空間
-
使用內容空間
-
釋放內容空間
而 js 垃圾是指對象不在引用時、對象不能從根上訪問到時,都可以被稱為 js 垃圾。其他部分包括引用和可達對象這些大家肯定很熟悉了,我們就不再多說。下面我們談一談 GC 算法。
## GC 算法
GC 算法其實是為了找到內存中的垃圾,并釋放和回收空間。這里所說的的垃圾,是指算法中認為程序中不再需要使用的對象,與程序中不能訪問到的對象。
說回 GC 算法,這個是比較概念性的內容,我們簡單歸納一下。GC 是一種自內存中查找垃圾釋放空間、回收空間的一個垃圾回收器機制。算法則是工作時查找和回收所遵循的規則。常見 GC 算法有引用計數、標記清除、標記整理、分代回收。
引用計數
引用計數曾經主要用于 IE8 以下的瀏覽器,現在的瀏覽器已不再使用,因此只做簡單介紹。引用計數的基本原理是記錄跟蹤每一個值被引用的次數,被引用則計數加一,被釋放則減一,當數值為零時則代表該值所在內存已經不再使用,因此釋放所占空間。引用計數的優點是引用次數實時監控,所以回收垃圾能夠及時回收,從而最大限度減少程序暫??D時間。但也是因為一直在運作,所以資源消耗和時間開銷大,無法回收循環引用的對象。
標記清除
標記清除分為分為標記和清除兩個階段,其核心思想是遍歷所有對象,找標記活動對象,即前面提到的可達對象,清除沒有標記的對象,以及回收沒有標記對象的空間。
上圖是 global 的查找簡易流程圖。其中左側 A、B、C、D、E 表示可查到的對象,右側 a1、b1 表示循環引用對象。其中 a1 為引用計數,而因為引用計數一直在運作,無法回收循環引用的對象的缺點,可以反向找到正在循環引用的對象。
這也是標記清除的優點,可以解決對象循環的引用回收問題。但是標記清除的缺點是空間碎片化,無法及時回收垃圾對象。因為它需要先標記再清除,不能像引用計數一樣對值進行實時監控,因此無法讓空間最大化使用。通過下圖可以簡單看一下標記清除的空間碎片化特點。
標記整理
上面提到標記清除有空間碎片化的缺點,而標記整理優化了這個缺點。從名字也可以聯想到,標記整理是標記清除的增強。標記整理在標記階段的操作和標記清除一致,但是在清除階段會先執行整理,再進行清除。這種方式能夠有效減少碎片化空間。和標記清除一樣,標記整理也不能實時回收垃圾對象。
我們通過下面三張圖對標記整理進行一個簡單直觀的了解。
可以看到在進行垃圾回收前,活動空間和非活動空間是混雜的。而在確定進行回收后,標記整理會對空間進行歸類整理,將活動空間和非活動空間統一整理到一起,形成下圖的結果:
之后再進行標記清除就能夠避免回收操作避免出現大量碎片化空間,讓空間最大化應用。
看完了 GC 算法,以 V8 引擎為例我們具體來看一下 GC 算法在 JS 垃圾回收里的使用。
V8 引擎的垃圾回收
V8 是一款當下較為主流 JavaScript 執行引擎,采用即時編譯,處理速度很快。V8 的內存是設限的,比如 64 位操作系統的上限是 1.4T,下限是 700M,32 位操作系統的上下限分別為 64M 和 32M。
V8 采用分代回收的垃圾回收策略,將內存分為新生代和舊生代兩種,并對不同的對象采用不同的對應算法。
上圖是 V8 的內存分配示意圖,可以清除看到 V8 內存空間分為兩部分。左邊的 from 和 to 是新生代,占用的空間比較小(32M|16M),這里的新生代指的是存活時間短的存儲區。右邊紅色的部分則是存活時間較長的老生代存儲區。
V8 常用的 GC 算法有以下 5 種:
-
分代回收
-
空間復制
-
標記清除
-
標記整理
-
標記增量
這其中新生代采用復制算法和標記整理進行垃圾回收,老生代使用標記清除、標記整理和增量標記進行垃圾回收。
V8 新生代對象回收實現
上圖為 V8 新生代對象回收實現圖,采用復制算法和標記整理結合的方式進行垃圾回收。新生代內存區的兩個等大空間,From 代表使用空間用于存儲活動對象,To 代表空閑空間。V8 的新生代對象回收是通過標記整理將對象完成整理后拷貝到 To,然后將 To 和 From 進行空間交換,并釋放整理后的無用對象所占空間。需要注意的是,在將整理對象拷貝到 To 時可能會出現晉升。晉升指的是將新生代對象移動至老生代存儲區。晉升通常有兩個條件,其一是在進行一輪 GC 后還活著的新生代對象可以晉升,其二是 To 空間的使用率超過 25%。
V8 老生代對象回收實現
V8 老生代的回收過程采用標記清除、標記整理和標記增量結合的方式。一般在進行垃圾回收時會通過標記清除完成垃圾空間的回收,但是當新生代移動到老生代,而老生代內存不夠時,則會通過標記整理進行空間優化,并使用增量標記進行效率優化。
標記增量其實是通過對標記操作進行標記的方法,讓時間安排變得合理。這句話可能有些繞,簡單說就是在垃圾回收時,讓標記系統在標記時分出不同的時間段,分別進行標記和執行,讓二者的操作間隔開,從而優化時間安排,這會讓頁面在體感上更為順暢。
總結
以上是生活随笔為你收集整理的javaScript 内存管理机制的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 悲报, GIF 之父因新冠去世
- 下一篇: JNI 技巧