亚洲欧美第一页_禁久久精品乱码_粉嫩av一区二区三区免费野_久草精品视频

蟲(chóng)蟲(chóng)首頁(yè)| 資源下載| 資源專(zhuān)輯| 精品軟件
登錄| 注冊(cè)

您現(xiàn)在的位置是:首頁(yè) > 技術(shù)閱讀 >  編程思考:對(duì)象生命周期的問(wèn)題

編程思考:對(duì)象生命周期的問(wèn)題

時(shí)間:2024-02-12




前情提要


只要寫(xiě)過(guò) c/c++ 的項(xiàng)目的童鞋應(yīng)該對(duì)對(duì)象生命周期的問(wèn)題記憶猶新。怕有人還不理解這個(gè)問(wèn)題,筆者先介紹下什么是生命周期的問(wèn)題?

一個(gè) struct 結(jié)構(gòu)體生命周期分為三個(gè)步驟:

  1. 出生:malloc 分配結(jié)構(gòu)體內(nèi)存,并且初始化;
  2. 使用:這個(gè)就是對(duì)內(nèi)存的常規(guī)使用了;
  3. 銷(xiāo)毀:free 釋放這個(gè)內(nèi)存塊;

最典型結(jié)構(gòu)體“生命周期”問(wèn)題的場(chǎng)景就是:你在使用對(duì)象正嗨的時(shí)候,被人偷偷把對(duì)象銷(xiāo)毀了。舉個(gè)例子:

  • 12:00 時(shí)刻:ObjectA 內(nèi)存 malloc 出來(lái),地址為 0x12345 ;
  • 12:10 時(shí)刻:ObjectA 內(nèi)存地址 0x12345 釋放了;
  • 12:12 時(shí)刻:程序猿小明拿到了 ObjectA 的地址 0x12345 ,準(zhǔn)備大干一場(chǎng)(但他并不知道的是,這個(gè) ObjectA 結(jié)構(gòu)體已經(jīng)結(jié)束了生命,0x12345 地址已經(jīng)被釋放了)于是,踩內(nèi)存了,全劇終;

生命周期問(wèn)題的維度


一般來(lái)講,生命周期的問(wèn)題其實(shí)有兩個(gè)方面:

  • 第一個(gè)是結(jié)構(gòu)體本身內(nèi)存的生命周期 ;
  • 第二個(gè)是結(jié)構(gòu)體對(duì)象管理的資源( 比如資源句柄 );

 1   結(jié)構(gòu)體本身內(nèi)存


對(duì)象結(jié)構(gòu)體本身的生命周期這個(gè)很容易理解,這個(gè)就是內(nèi)存的分配和釋放。

// 步驟一:分配
obj_addr = malloc(...);
// 步驟二:使用 ...
// 步驟三:釋放
free(obj_addr);

如果違反了這條(使用了已經(jīng)釋放的內(nèi)存塊),就會(huì)發(fā)生踩內(nèi)存,野指針,未定義地址等一系列奇異事件。如果沒(méi)正確釋放,那么就是內(nèi)存泄漏。


 2   對(duì)象管理的資源


這個(gè)也很容易理解,比如一個(gè)代表 fd_t 的結(jié)構(gòu)體,里面有一個(gè)整型字段,代表這個(gè)結(jié)構(gòu)體管理的一個(gè)文件句柄。當(dāng) fd_t 結(jié)構(gòu)體內(nèi)存被釋放的時(shí)候,它管理的文件句柄 sys_fd 也是需要 close 的。

struct fd_t {
    int sys_fd; // 系統(tǒng)句柄(這個(gè)需要在合適的時(shí)機(jī)釋放)
    struct list_head list; // 鏈表掛接件
    //...
};

如果違反了這條(使用釋放了的資源,比如句柄),那么就會(huì)出現(xiàn) bad descriptor 等一系列情況。


怎么才能解決生命周期的問(wèn)題?


生命周期的問(wèn)題是每個(gè)程序猿都可能遇到的,只要程序中涉及到資源的創(chuàng)建、使用、釋放,這三個(gè)過(guò)程,那么生命周期的問(wèn)題就是你必經(jīng)之路,這是一個(gè)通用的問(wèn)題

上面我們提到生命周期問(wèn)題的兩個(gè)維度,那么解決也是這兩個(gè)維度的針對(duì)性解決。遵守兩個(gè)原則

  1. 對(duì)象在有人使用的時(shí)候不能釋放;
  2. 對(duì)象不僅要釋放自身內(nèi)存還要釋放管理的資源

思考下:你在編程的時(shí)候,怎么處理的?

下面我從 c 這種底層語(yǔ)言,還有 Go 這種自帶 GC 的語(yǔ)言對(duì)比出發(fā),來(lái)體驗(yàn)下不同語(yǔ)言下的生命周期的問(wèn)題怎么解決。


c 編程的慣例


c 怎么才能保證內(nèi)存的安全,資源的安全釋放呢?

以下面的場(chǎng)景舉例:

  1. 現(xiàn)在有一個(gè) fd_t 的 list 鏈表,為了保護(hù)這個(gè)鏈表,用一個(gè)互斥鎖來(lái)保護(hù) ;
  2. 創(chuàng)建 fd_t 的時(shí)候,需要添加進(jìn) list(添加會(huì)加互斥鎖);
  3. 正常使用的時(shí)候,會(huì)遍歷 list ,取合適的元素使用;
  4. fd_t 銷(xiāo)毀的時(shí)候,會(huì)從全局鏈表中摘除;

首先,list 鏈表的并發(fā)安全可以用互斥鎖來(lái)解決,但是怎么保證你取出來(lái)元素之后,還在處理的時(shí)候,一直是安全的呢(不被釋放)?

你可能會(huì)自然想到一個(gè)思路:全程在鎖內(nèi)不就可以了。

確實(shí)如此,對(duì)象的創(chuàng)建,使用,刪除,全程用鎖保護(hù),確實(shí)可以解決這個(gè)問(wèn)題。但是鎖度變得非常大,在現(xiàn)實(shí)生產(chǎn)環(huán)境的編程中,很少見(jiàn)。

其實(shí),解決資源釋放的場(chǎng)景,有一個(gè)通用的技術(shù):引用計(jì)數(shù)。 wiki 上的解釋?zhuān)?/p>

引用計(jì)數(shù)是計(jì)算機(jī)編程語(yǔ)言中的一種內(nèi)存管理技術(shù),是指將資源(可以是對(duì)象、內(nèi)存或磁盤(pán)空間等等)的被引用次數(shù)保存起來(lái),當(dāng)被引用次數(shù)變?yōu)榱銜r(shí)就將其釋放的過(guò)程。使用引用計(jì)數(shù)技術(shù)可以實(shí)現(xiàn)自動(dòng)資源管理的目的。

引用計(jì)數(shù)是一種通用的資源管理技術(shù),簡(jiǎn)述引用計(jì)數(shù)用法:

  1. 資源初始化的時(shí)候,計(jì)數(shù)為 1 ;
  2. 就是在資源獲取的時(shí)候,對(duì)資源計(jì)數(shù)加 1 ;
  3. 資源使用完成的時(shí)候,對(duì)資源計(jì)數(shù)減 1 ;
  4. 計(jì)數(shù)為 0 的時(shí)候,走釋放流程 ;

這樣,只需要用戶對(duì)資源的使用上遵守一個(gè)規(guī)則:獲取的時(shí)候,計(jì)數(shù)加 1,處理完了,計(jì)數(shù)減 1 ,就能保證不會(huì)有問(wèn)題。因?yàn)樵谀闶褂闷陂g,不管別人怎么減,都不可能會(huì)到 0 。

思考下:引用計(jì)數(shù)有什么缺點(diǎn)呢?

  1. 第一個(gè)問(wèn)題,非常容易出錯(cuò),加減引用一定要配對(duì),一旦有些地方多加了,或者多減了,就會(huì)引發(fā)資源問(wèn)題。要么就是泄漏,要么就是使用釋放了的資源;
  2. 第二個(gè)問(wèn)題,在于流程上變復(fù)雜了,因?yàn)?strong>計(jì)數(shù)為 0 的地方點(diǎn)變得不確定了。可能會(huì)出現(xiàn)在讀元素的流程上,走釋放流程;

以上兩點(diǎn),其實(shí)對(duì)程序猿的能力、細(xì)致提出了很高的要求。


Go 就厲害了


引用計(jì)數(shù)是通用的技術(shù),適用于所有的語(yǔ)言。筆者在寫(xiě) Go 的時(shí)候就用引用計(jì)數(shù)來(lái)解決過(guò)資源釋放的問(wèn)題。

但后來(lái)發(fā)現(xiàn),Go 語(yǔ)言其實(shí)可以把代碼寫(xiě)的更簡(jiǎn)單,Go 的創(chuàng)建則從兩個(gè)的角度解決了對(duì)象生命周期的問(wèn)題:

第一,根本不讓用戶釋放內(nèi)存;

Go 的內(nèi)存,程序猿只能觸發(fā)分配,無(wú)法主動(dòng)釋放。釋放內(nèi)存的動(dòng)作完全交給了后臺(tái) GC 流程。這就很好的解決了第一個(gè)問(wèn)題,由于不讓粗心的程序猿參與到資源的管理中,內(nèi)存資源的管理完全由框架管理(框架強(qiáng),則我強(qiáng),嘿嘿),根本就不用擔(dān)心會(huì)被程序猿用到生命終結(jié)的內(nèi)存塊。

第二,提供析構(gòu)回調(diào)函數(shù)機(jī)制;

上面說(shuō)了,GC 能夠保證內(nèi)存結(jié)構(gòu)體本身的安全性,但是一些句柄資源的釋放卻無(wú)法通過(guò)上面保證,怎么辦?

Go 提供了一個(gè)非常好的辦法:設(shè)置析構(gòu)函數(shù)。使用 runtime.SetFinalizer 來(lái)設(shè)置,將一個(gè)對(duì)象的地址和一個(gè)析構(gòu)函數(shù)綁定起來(lái),并且注冊(cè)到框架里。當(dāng)對(duì)象被 GC 的時(shí)候,析構(gòu)函數(shù)將會(huì)被框架調(diào)用,程序猿則可以把資源釋放的邏輯寫(xiě)到析構(gòu)函數(shù)中,這樣就配合上了呀,就能保證:在對(duì)象永遠(yuǎn)不能被程序猿摸到的前提下,調(diào)用了析構(gòu)函數(shù),從而完成資源釋放


 1   生命結(jié)束的回調(diào)


函數(shù)原型:

func SetFinalizer(obj interface{}, finalizer interface{})

參數(shù)解析:

  • 參數(shù) obj 必須是指針類(lèi)型
  • 參數(shù) finalizer 是一個(gè)函數(shù),參數(shù)為 obj 的類(lèi)型,無(wú)返回值

函數(shù)調(diào)用 runtime.SetFinalizerobjfinalizer 關(guān)聯(lián)起來(lái)。對(duì)象 obj 被 Gc 的時(shí)候,Go 會(huì)自動(dòng)調(diào)用 finalizer 函數(shù),并且 obj 作為參數(shù)傳入。

就這樣,關(guān)于生命周期的問(wèn)題,在 Go 里面就非常優(yōu)雅的解決了,對(duì)象內(nèi)存釋放交給了 Gc,資源釋放交給了 finalizer ,程序猿又可以躺好了。


擴(kuò)展思考


c++ 和 Python 這兩種語(yǔ)言又是怎么解決內(nèi)存的生命周期,還有資源的安全釋放呢?

提示:這兩種語(yǔ)言都有構(gòu)造函數(shù)和析構(gòu)函數(shù),但各有不同。這個(gè)問(wèn)題留給讀者朋友思考。

  • c++ 有構(gòu)造函數(shù)和析構(gòu)函數(shù),也很方便,但是 c++ 的類(lèi)卻是非常復(fù)雜的。且 c++ 是沒(méi)有 GC 的,內(nèi)存釋放的動(dòng)作還是交給了程序猿,所以在 c++ 編程中,引用計(jì)數(shù)技術(shù)還是大量使用的;
  • python 是一個(gè)自帶 GC ,并且提供構(gòu)造和析構(gòu)函數(shù)的。所以 python 的使用,程序猿完全不管內(nèi)存釋放,資源釋放則只需要定義在類(lèi)的析構(gòu)函數(shù)里即可;

總結(jié)


  1. 生命周期的問(wèn)題是老大難的問(wèn)題,分為結(jié)構(gòu)內(nèi)存的安全釋放,內(nèi)部管理資源的安全釋放兩個(gè)維度;
  2. c/c++ 大量采用引用計(jì)數(shù)技術(shù)來(lái)完成對(duì)資源的安全釋放;
  3. 引用計(jì)數(shù)的難點(diǎn)在于加減計(jì)數(shù)的配套使用,并且釋放的現(xiàn)場(chǎng)不確定
  4. Go 通過(guò)內(nèi)存自動(dòng) Gc ,且提供析構(gòu)函數(shù)綁定到對(duì)象地址的方法,從而完美解決了對(duì)象生命周期的問(wèn)題;
  5. runtime.SetFinalizer 替代引用計(jì)數(shù)的使用,太香了;

后記


open  一個(gè)文件得到句柄 fd,緊接 unlink  這個(gè)文件,此時(shí),還可用 fd 來(lái)正常讀寫(xiě)文件。直到 close  這個(gè)文件的時(shí)候,這個(gè)文件才會(huì)永遠(yuǎn)的消失。你能猜到其中原理嗎?

~完~

往期推薦



往期推薦



自制文件系統(tǒng) —— 03 Go實(shí)戰(zhàn):hello world 的文件系統(tǒng)

假如 Go 能說(shuō)話,聽(tīng)聽(tīng) GMP 的心聲

存儲(chǔ)基礎(chǔ) — 文件描述符 fd 究竟是什么?

深度剖析 Linux cp 的秘密


堅(jiān)持思考,方向比努力更重要。關(guān)注我:奇伢云存儲(chǔ)


主站蜘蛛池模板: 九龙县| 井冈山市| 临沧市| 方正县| 甘泉县| 确山县| 海南省| 广宗县| 汽车| 阿巴嘎旗| 南开区| 大厂| 云阳县| 昌邑市| 上高县| 平陆县| 明星| 石台县| 彭山县| 碌曲县| 泰安市| 剑川县| 虎林市| 峨眉山市| 商河县| 洛南县| 鹿泉市| 台东县| 鄂温| 台前县| 共和县| 大同县| 浮山县| 水城县| 古田县| 德钦县| 德令哈市| 光山县| 怀来县| 百色市| 吴堡县|