程式的執行需要記憶體,對於一些需要持續執行很久的程式,尤其是伺服器程式,如果不及時釋放掉不再需要的記憶體,就會導致記憶體堆中的佔用持續走高,最終可能導致程式崩潰。
不再需要使用的記憶體,卻一直佔用著空間,得不到釋放,這就叫記憶體洩漏。
在JS中,我們都知道,基本資料型別是存在棧(stack)中,而引用資料型別是存在堆(heap)中。存在棧中的資料,會被自動處理掉。但存在堆中的資料則不然。
JS引擎有個垃圾回收機制,可以幫助我們來清除不需要的資料。關鍵來了,我們怎麼告訴JS引擎這個我不再需要這個資料呢?
答案就是:切斷它的引用,讓它變成一座無法到達的島嶼。
這就是所謂的“標記清除”。也就是說,我們對堆中的引用型別資料做一個遍歷,標記一下它們是否是可以被外部訪問到的:
let a = {name:'zhang'}; let b = a; let c = {name:'zhao'}; c = null;
在上面的這段程式碼中,{name:'zhang'}這個物件分別被變數a和變數b引用。{name:'zhao'}這個物件一開始被變數c引用,後來c被重新賦值,{name:'zhao'}這個物件的引用突然被切斷,再也無法被訪問到了,自己變成了一個“孤島式資料”,無法被外部訪問,被當成了垃圾。
而垃圾的宿命,就是被JS引擎回收。
我們來執行一下上述這段程式碼,每點選一次,就給obj存入1000個不同物件的引用,連續點選10s,停止30s。並把這個過程錄製下來。
怎麼錄?開啟chrome的performance即可錄製,看看這40s的記憶體堆(JS heap)的走勢。
(結果是令人寒心的,堆記憶體佔用從15M左右飆到最高30M,且基本沒有什麼回落,居高不下)
現在我們把程式碼改一改,在末尾增加一句this.obj = []。用於清除本次操作產生的1000個引用。
按照同樣的手法,點選10s,停止30s,總共錄製40s。效果如下:
(可以看到中途最高飆到24.7M,但是垃圾回收在第5s和第10s都介入了一次,使得記憶體佔用大幅減少)
可以看到垃圾回收還是很有用的,同時也可以看到,垃圾回收並不是無時無刻都在進行,因為垃圾回收這個操作也有效能損耗,從我實測的結果來看,它是按照一定的時間間隔進行的。