垃圾回收器是一把十足的雙刃劍。其好處是可以大幅簡化程序的內存管理代碼,因為內存管理無需程序員來操作,由此也減少了(但沒有根除)長時間運轉的程序的內存泄漏。對于某些程序員來說,它甚至能夠提升代碼的性能。
另一方面,選擇垃圾回收器也就意味著程序當中無法完全掌控內存,而這正是移動終端開發(fā)的癥結。對于JavaScript,程序中沒有任何內存管理的可能——ECMAScript標準中沒有暴露任何垃圾回收器的接口。網(wǎng)頁應用既沒有辦法管理內存,也沒辦法給垃圾回收器進行提示。
嚴格來講,使用垃圾回收器的語言在性能上并不一定比不使用垃圾回收器的語言好或者差。在C語言中,分配和釋放內存有可能是非常昂貴的操作,為了使分配的內存能夠在將來釋放,堆的管理會趨于復雜。而在托管內存的語言中,分配內存往往只是增加一個指針。但隨后我們就會看到,當內存耗盡時,垃圾回收器介入回收所產生的巨大代價。一個未經(jīng)琢磨的垃圾回收器,會致使程序在運行中出現(xiàn)長時間、無法預期的停頓,這直接影響到交互系統(tǒng)(特別是帶有動畫效果的)在使用上的體驗。引用計數(shù)系統(tǒng)時常被吹捧為垃圾回收機制的替代品,但當大型子圖中的最后一個對象的引用解除后,同樣也會有無法預期的停頓。而且引用計數(shù)系統(tǒng)在頻繁執(zhí)行讀取、改寫、存儲操作時,也會有可觀的性能負擔。
或好或壞,JavaScript需要一個垃圾回收器。V8的垃圾回收器實現(xiàn)現(xiàn)在已經(jīng)成熟,其性能優(yōu)異,停頓短暫,性能負擔也非常可控。
基本概念
垃圾回收器要解決的最基本問題就是,辨別需要回收的內存。一旦辨別完畢,這些內存區(qū)域即可在未來的分配中重用,或者是返還給操作系統(tǒng)。一個對象當它不是處于活躍狀態(tài)的時候它就死了(廢話)。一個對象處于活躍狀態(tài),當且僅當它被一個根對象或另一個活躍對象指向。根對象被定義為處于活躍狀態(tài),是瀏覽器或V8所引用的對象。比如說,被局部變量所指向的對象屬于根對象,因為它們的棧被視為根對象;全局對象屬于根對象,因為它們始終可被訪問;瀏覽器對象,如DOM元素,也屬于根對象,盡管在某些場合下它們只是弱引用。
從側面來說,上面的定義非常寬松。實際上我們可以說,當一個對象可被程序引用時,它就是活躍的。比如:
function f() { var obj = {x: 12}; g(); // 可能包含一個死循環(huán) return obj.x; }def scavenge(): swap(fromSpace, toSpace) allocationPtr = toSpace.bottom scanPtr = toSpace.bottom for i = 0..len(roots): root = roots[i] if inFromSpace(root): rootCopy = copyObject(&allocationPtr, root) setForwardingAddress(root, rootCopy) roots[i] = rootCopy while scanPtr < allocationPtr: obj = object at scanPtr scanPtr += size(obj) n = sizeInWords(obj) for i = 0..n: if isPointer(obj[i]) and not inOldSpace(obj[i]): fromNeighbor = obj[i] if hasForwardingAddress(fromNeighbor): toNeighbor = getForwardingAddress(fromNeighbor) else: toNeighbor = copyObject(&allocationPtr, fromNeighbor) setForwardingAddress(fromNeighbor, toNeighbor) obj[i] = toNeighbor def copyObject(*allocationPtr, object): copy = *allocationPtr *allocationPtr += size(object) memcpy(copy, object, size(object)) return copy
新聞熱點
疑難解答
圖片精選