引用計數 vs. GC
記憶體管理問題
記憶體管理是程式設計過程中的一個經典問題,早期在 C 語言時代,幾乎都靠 malloc/free 手動管理記憶體。隨著各個平臺的發展,到現在被廣泛採用的主要有兩個方法:
- 引用計數 (ARC,Automatic Reference Counting)
- GC (Garbage Collection)
管理方法 ARC/GC
因為 Java 的流行,GC 被廣泛的認知。GC 簡單的說是定期查詢不再使用的物件,釋放物件佔用的記憶體。
基於 GC,申請的物件不需要手動釋放,只需要確認物件在不再需要時,不再被其他物件引用。
引用計數早期主要用於底層系統,比如檔案系統的 inode 管理,後來 C++ 的 boost 庫實現了一套完整的 ARC,目前流行的系統還有 Objective C 也是採用的 ARC。
ARC 的特點是,一個物件被引用時,引用計數增加 1,引用物件釋放時,引用計數減少 1,如果引用計數為 0,釋放物件。
比較
因為 ARC 和 GC 的不同策略,對程式設計幾個方面的影響如下。
效能
GC 需要一套額外的系統跟蹤分配的記憶體,分析哪些記憶體需要釋放,相對來說就需要更多的計算。這也是為什麼對效能敏感的場景不採用 GC 的原因,比如,高效能的服務端程式,資源有限的嵌入式裝置(iOS 就沒有采用 GC)。
ARC 由開發者自己來管理資源在什麼時候釋放,不需要額外的資源,所以效能沒有損失。
延遲
GC 回收記憶體時,需要完全暫停當前程式,這會給程式帶來難以預測的一個延遲期。如果需要回收的資源很多,這個延遲可能會非常大。
ARC 在資源引用為 0 時立即釋放,沒有不可預測的延遲。
程式設計難度
不難看出,GC 在效能、延遲等方面有明顯的缺點,為什麼 GC 還會被廣泛採用呢?
GC 帶來的最大好處是不需要開發者手動管理記憶體分配,這大大降低了程式設計難度,同時可以大幅減少跟記憶體管理相關的 Bug:
- 懸空指標。指標指向的記憶體被其他程式碼釋放
- 重複釋放記憶體
- 記憶體洩漏。申請的記憶體沒釋放
不過使用 GC 並不代表可以完全不用理解記憶體管理,如果物件的引用關係跟想象的不一致,GC 也會有記憶體洩漏的問題
。
我們之前理解的記憶體洩漏
是指一個分配的記憶體沒有被釋放造成的。而 GC 平臺下的記憶體洩漏是指物件有引用而開發者不知道
,比如:
ObjectA -> ObjectB
ObjectB 使用完後,我們沒有及時把 ObjectA 引用 ObjectB 的指標設定為 NULL,這時, ObjectB 不會被 GC 回收。
- 對比表格
| | 時機| 效能 | 延遲 | 程式設計難度| | :--:|:---:| :----:| :-----:| :-----:| | ARC | 引用計數為 0 馬上回收 | 快 | 小 | 較大 | | GC | 定時掃描清理 | 慢 | 大| 較小 |
怎麼選擇 ARC or GC
開發一個專案時,採用什麼樣的平臺,跟實際面對的場景有很大關係,沒有一個技術是用來解決所有問題的。
一般來說,對延遲和效能不敏感的系統,可以考慮帶 GC 的平臺,比如 Java、Go 等來開發,通常可以提高開發效率。
如果需要對系統的效能有良好的控制,或者平臺的資源有限,ARC 是更好的選擇。比如作業系統、資料庫等選擇 C 或者 C++。比如 iOS 的 Object C 就是採用 ARC,實際來看比使用 Java (GC) 的 Android 平臺的表現要好太多。
但是 ARC 平臺一般對開發者要求要更高。
最近出現的新語言Rust
採用的是 ARC,但是 Rust 會在程式碼編譯階段對記憶體、指標的使用做嚴格的分析和檢查,確保程式沒有記憶體管理問題。相當於把 GC 的一部分工作移到編譯階段,這樣程式的執行效能幾乎沒有損失,同時又大大減少記憶體管理相關的 Bug。
我的觀察從C++11
正式吸納boost
的 smart pointer
後,C++ 在記憶體管理方面比之前有極大的提升,如果嚴格的按照 smart pointer 的規範,同樣可以減少記憶體管理的風險。Rust 就有點像一個嚴格的 C++11 編譯系統。
支援 GC 的平臺裡面有一個特殊的,就是 Erlang
。Erlang 的 GC 是程式級別的(Erlang 的輕量級程式),意味著 GC 發生時,只暫停當前程式,其他程式不受影響。另外,Erlang 程式往往會執行海量的程式,相當於把 GC 分散開了,所以 Erlang 的 GC 一般不會產生明顯的延遲。
瞭解這些細節,在面對具體問題時,能幫你做出正確的選擇。
歡迎來微博留下您的意見 微博連結:http://weibo.com/2255454164/E48lBE9pU
weibo: @Tiger_張虎, 雲巴 (yunba.io) 創始人,yunba.io 雲端實時訊息服務。 JPush 創始人,原CTO。 Oracle VM 創始團隊成員。
相關文章
- 怎麼解決引用計數 GC 的迴圈引用問題?GC
- Java虛擬機器-GC垃圾回收演算法-引用計數法Java虛擬機GC演算法
- swift自動引用計數Swift
- 物件的引用計數與dealloc物件
- Java引用計數與實現Java
- iOS引用計數管理之揭祕計數儲存iOS
- Airflow vs. Luigi vs. Argo vs. MLFlow vs. KubeFlowAIUIGo
- GC判定與回收演算法+java物件引用型別GC演算法Java物件型別
- Netty原始碼分析之ByteBuf引用計數Netty原始碼
- PHP的引用計數是什麼意思?PHP
- PHP的垃圾回收機制-引用計數PHP
- 垃圾回收演算法:引用計數法演算法
- Objective-C自動引用計數ARCObject
- C++ :引用計數(reference count) 實現C++
- 引用變數變數
- 智慧指標引用計數變化學習指標
- HashSet vs. TreeSet vs. LinkedHashSet
- iOS 開發刷題系列三:NSString 引用計數iOS
- 幽默:網管 vs. 程式設計師程式設計師
- C++智慧指標學習——小談引用計數C++指標
- C#程式設計:ref【引數按引用傳遞】C#程式設計
- 垃圾回收的引用計數器演算法詳解演算法
- MySQL 驅動中虛引用 GC 耗時最佳化與原始碼分析MySqlGC原始碼
- 過早的給方法中 引用物件 設為 null 可被 GC提前回收嗎?物件NullGC
- [譯] 斐波那契數列中的偶數 (Python vs. JavaScript)PythonJavaScript
- 深入理解JVM虛擬機器-物件引用,GC與記憶體分配回收JVM虛擬機物件GC記憶體
- Full GC (Metadata GC Threshold)GC
- Rust 程式設計影片教程(進階)——013 使用 Rc 引用計數智慧指標Rust程式設計指標
- 搞懂:資料科學vs.機器學習vs.資料分析vs.商業分析資料科學機器學習
- project中的堆疊記憶體,記憶體地址引用,gc相關問題Project記憶體GC
- Ansible vs. TerraformORM
- 後端程式設計師一定要看的語言大比拼:Java vs. Go vs. Rus後端程式設計師JavaGo
- Navigating Kubernetes Certification: CKAD vs. CKA vs. CKS, Including KCNA and KCSA
- 從CLR GC到CoreCLR GCGC
- less 全域性變數使用 引用變數
- JavaScript中的安全變數引用JavaScript變數
- Makefile引用與環境變數變數
- Rust 程式設計視訊教程(進階)——013 使用 Rc 引用計數智慧指標Rust程式設計指標
- C#7.0--引用返回值和引用區域性變數C#變數