詳解Python和Rust中內(nèi)存管理機制的實現(xiàn)與對比
大家好,內(nèi)存管理是編程語言的基礎,它確保著資源被有效利用,不同的編程語言采用不同的策略來管理內(nèi)存。有些語言需要程序員手動管理內(nèi)存,有些語言則自動化了內(nèi)存管理過程。Python和Rust都采用了垃圾收集(Garbage Collection)機制來管理內(nèi)存,但它們各自的實現(xiàn)方式有很大的不同。
1.Python:引用計數(shù)與分代式垃圾收集
Python使用稱為“引用計數(shù)”的技術進行垃圾收集,每個對象都有一個計數(shù)器,跟蹤對其的引用數(shù)量。當此計數(shù)達到零時,對象就會從內(nèi)存中刪除。
換句話說,每個內(nèi)存中的對象都有一個關聯(lián)的數(shù)字(稱為“引用計數(shù)”),跟蹤它被多少變量或其他對象指向。
import sys # 創(chuàng)建對象x x = [1, 2, 3] # 獲取x的引用計數(shù)(應該是1) print("Reference Count of x:", sys.getrefcount(x) - 1) # 創(chuàng)建x的引用 y = x # 引用計數(shù)增加 1 print("Reference Count of x after y = x:", sys.getrefcount(x) - 1) # 刪除引用 del y # 引用計數(shù)減少 1 print("Reference Count of x after del y:", sys.getrefcount(x) - 1)
輸出:
Reference Count of x: 1
Reference Count of x after y = x: 2
Reference Count of x after del y: 1
Python采用稱為“分代垃圾收集”的生成式方法,來進一步提高垃圾收集的效率。對象分為三個不同的“代”:
Generation 0:新對象
對象最初分配在第0代,這是其生命周期的第一階段。
# 導入 gc(垃圾回收)模塊 import gc # 啟用調(diào)試以打印垃圾收集信息 gc.set_debug(gc.DEBUG_STATS) # 創(chuàng)建一個新的列表對象;該對象最初處于第 0 代 new_object = [1, 2, 3] # 僅在第 0 代手動運行垃圾回收 gc.collect(0)
創(chuàng)建new_object時,它是一個新對象,會在第0代開始其生命
Generation 1:經(jīng)歷過一次垃圾回收周期的對象
在第0代垃圾回收周期中未被回收的對象會轉(zhuǎn)移到第1代。
# 創(chuàng)建一個持久對象 persistent_object = {"key": "value"} # 在第 0 代運行垃圾回收 gc.collect(0) # 此時,"persistent_object "存活并進入第 1 代
在Python的分代式垃圾收集中,當一個對象首次建時,它會被放入第0代。每當對該代進行垃圾收集循環(huán)時,Python會尋找不再需要的對象(即引用計數(shù)為零的對象),以刪除它們并釋放內(nèi)存。
如果像persistent\_object一樣的對象在這個垃圾收集周期中存活下來,這意味著它仍在被引用或使用,它就會 "老化 "并進入下一代,在這種情況下是第1代。
這背后的原理是,新創(chuàng)建的對象更有可能是短命的,會很快被垃圾回收。另一方面,如果一個對象已經(jīng)經(jīng)歷過一次垃圾回收周期,那么它就更有可能是長壽的,因此它會被轉(zhuǎn)移到較老的一代,接受較少頻率的檢查。
由于persistent_object仍在使用中(在代碼中的某個地方被引用),它的引用計數(shù)并不為零,因此它能在第0代垃圾回收過程中存活下來。
Generation 2:存活超過一個垃圾回收周期的對象
經(jīng)過多次垃圾收集周期的對象最終會轉(zhuǎn)到第2代。
# 創(chuàng)建另一個持久對象 very_persistent_object = (1, 2, 3) # 對第 0 代和第 1 代進行垃圾回收 gc.collect(0) gc.collect(1) # 此時,"very_persistent_object "存活下來,并應進入第 2 代
在這里,very_persistent_object在第0代和第1代的垃圾回收中都能存活,因此它將轉(zhuǎn)移到第2代。
實際上,開發(fā)者通常不需要手動控制或監(jiān)控這些生成,Python 的垃圾回收器會自動處理,但了解它們的工作原理對調(diào)試和優(yōu)化很有幫助。
2.Rust:基于所有權(quán)和借用的內(nèi)存管理
Rust的內(nèi)存管理方式與具有垃圾收集的語言(如Python)有著本質(zhì)區(qū)別,它依賴于“所有權(quán)”和“借用”的概念來確保資源的安全管理。
2.1 所有權(quán)
在Rust中,每個值都有一個“所有者”,該值在其所有者存在時有效。當所有者超出范圍時,該值及其資源會被自動釋放,這樣就不需要單獨的垃圾回收過程了。
以下是個示例:
fn main() { let s1 = String::from("hello"); // s1是值"hello"的所有者 let s2 = s1; // s1的所有權(quán)被傳遞給s2 // println!("{}", s1); // 這將導致錯誤,因為s1不再擁有該值 println!("{}", s2); // 這是可以的,s2現(xiàn)在是所有者 } // s2超出范圍,“hello”被釋放
在本例中,s1最初擁有字符串 "hello",然后所有權(quán)轉(zhuǎn)移到s2。當s2在main()的結(jié)尾超出范圍時,字符串"hello"會被自動釋放。
2.2 借用
有時,需要訪問一個值而不需要取得其所有權(quán),因此 Rust 允許 "借用",可以借用一個可變或不可變的引用值。
不可變借用:
fn main() { let s1 = String::from("hello"); let len = calculate_length(&s1); // &s1借用s1而不擁有它 println!("'{}'的長度為{}.", s1, len); } fn calculate_length(s: &String) -> usize { s.len() }
可變借用:
fn main() { let mut s1 = String::from("hello"); change_string(&mut s1); // &mut s1以可變引用形式借用s1 println!("{}", s1); } fn change_string(s: &mut String) { s.push_str(", world"); }
在這些示例中,&s1和&mut s1借用了值,但沒有取得所有權(quán),從而允許 s1在函數(shù)調(diào)用后繼續(xù)使用。
Rust 方法的主要優(yōu)勢在于,它可以精確控制代碼的哪些部分可以使用、修改或取消分配值,從而使程序更安全、更高效,而無需進行垃圾收集。
到此這篇關于詳解Python和Rust中內(nèi)存管理機制的實現(xiàn)與對比的文章就介紹到這了,更多相關Python內(nèi)存管理機制內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
pandas pd.cut()與pd.qcut()的具體實現(xiàn)
本文主要介紹了pandas pd.cut()與pd.qcut()的具體實現(xiàn),文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2023-01-01使用Python建立RNN實現(xiàn)二進制加法的示例代碼
這篇文章主要介紹了使用Python建立RNN實現(xiàn)二進制加法的示例代碼,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2021-03-03詳解Python Matplotlib解決繪圖X軸值不按數(shù)組排序問題
這篇文章主要介紹了詳解Python Matplotlib解決繪圖X軸值不按數(shù)組排序問題,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2019-08-08PyQT中QTableWidget如何根據(jù)單元格內(nèi)容設置自動寬度
這篇文章主要介紹了PyQT中QTableWidget如何根據(jù)單元格內(nèi)容設置自動寬度問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2023-05-05