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