1.可達的內存
v8 本身有內存回收機制,並且會回收 所有不可達的內存,那麼什麼是 可達的內存呢?
從全局執行上下文出發,能找到的,就被認爲是可達的
如下所示, obj 就是 可達的,而 obj1 obj2 就是不可達的
var obj = {parent: obj}
function fn() {
const obj1 = {}
const obj2 = {}
obj1.name = obj2
obj2.name = obj1
}
而如下所示,user 就是可達的,name 屬性是不可達的
const user = {age: 1}
const list = [user.age] // 引用 + 1
function fn() {
const name = 'jack'
}
fn()
2. v8 中的內存機制
64位 1.5G 32位 800M
新生代 | 老生代 | |
內存大小(64位) | 32M | 14G |
內存大小(32位) | 16M | 700M |
執行的操作 | 複製、標記整理 | 標記清除、標記整理、增量標記 |
- 其中新生代 還分爲 兩個區域,一個是from,一個是to,專門用來執行 複製 操作
- 所謂的複製 操作 指的是 在 from 區域 中的內存要 用完之前,就會把 當前 可達的內存 複製一份,到 to 區域中去,這其中還 可以順便整理內存空間,使得獲得一個 比較大的連續的內存空間
- 然後 在一輪的 GC 之中還存活着的話,那就把 新生代的 內存放到 老生代去
- 在這之後,to 空間 就變成了 from 空間,而原來的 from 空間就會被清空,成爲 to 空間,也就是進行 調換
下面重點來簡單介紹一下 各種內存 處理的算法
執行過程 | 是否可以立即執行 | 缺點 | 優點 | |
引用計數 | 引用 計數器,判斷當前的引用數是否爲0,當爲0的話,就可以直接刪除 | 是,只要計數器爲 0,可以立即執行 |
1、無法回收循環引用的對象 2、 空間開銷大 |
|
標記清除 |
1、遍歷所有對象找標記活動對象 其實就是從代碼層面 去 找一遍能找到的對象, global 開始查找 找到了就進行標記 2、遍歷 所有對象清除沒有標記對象 然後 把所有沒有被標記的 給 刪除 |
否 |
1、容易導致 空間的碎片化,空間的不連續 2、不能立即回收對象,回收的時候,程序停止工作 |
解決了 循環引用的問題 |
標記整理 |
當想把 新生代 往老生代的空間移動,但是不足以 完成晉升,就會對老生代執行 和 標記清除一樣,但是會對空間進行整理,讓空間能夠出現連續 |
否 | 不能立即回收對象,回收的時候,程序停止工作,最長也就1s | 這裏的執行比較緩慢,畢竟是對 老生代這麼大的東西進行整理,所以會導致 js 的性能問題 |
標記增量 |
1、對需要進行清除的 內存進行 一點一點地進行標記,但是並不立刻刪除 2、然後 標記一點,就執行一段 js 代碼,接着再 標記一點,又去執行一點代碼(類比於 js 中的 requestIdeCallback) 3、等到標記完成了,對所有標記的 垃圾對象進行一次性刪除 |
否 | 執行的時候會導致 js 停止執行 | 並不會導致 js 一次性停止太長的時間,提高用戶體驗 |