• 正文
  • 相關(guān)推薦
申請(qǐng)入駐 產(chǎn)業(yè)圖譜

你知道程序的“腎”是什么嗎?

2020/03/03
100
加入交流群
掃碼加入
獲取工程師必備禮包
參與熱點(diǎn)資訊討論

在本文的開(kāi)頭,我必須聲明這是一篇有目的性的文章。我有一個(gè)長(zhǎng)輩最近得了尿毒癥,所以我聯(lián)系芯片之家的管理員并且得到他非常爽快的允許,在芯片之家的平臺(tái)將人體的腎功能與開(kāi)發(fā)技術(shù)結(jié)合起來(lái)寫一篇文章發(fā)表在這里,希望得到更多人的幫助。所以,請(qǐng)記?。翰还苣寝D(zhuǎn)發(fā)本文,點(diǎn)在看,還是點(diǎn)擊原文輕眾籌給予幫助或者轉(zhuǎn)發(fā),都是一次善舉。

程序的形態(tài)非常之多,不管是可以作為一個(gè)操作系統(tǒng),還是作為一個(gè) hello world,也不管是作為一個(gè) app,還是作為一個(gè)嵌入式固件。程序在本質(zhì)上來(lái)說(shuō),是函數(shù)代碼與資源的集合體。我們的話題是,程序的資源,而且是程序中第一要素的資源——內(nèi)存。

如果說(shuō)程序是一個(gè)人,那么骨架可以比喻成程序的架構(gòu),皮肉則是代碼,血液則是內(nèi)存。在程序的運(yùn)行過(guò)程當(dāng)中,絕大部分的指令在執(zhí)行與回寫操作,我必須聲明這是一篇有目的性的文章。所以我聯(lián)系芯片之家的管理員并且得到他非常爽快的允許,回寫階段都會(huì)操作到內(nèi)存,可以說(shuō)內(nèi)存伴隨著程序執(zhí)行的整個(gè)周期,就像是血液始終流轉(zhuǎn)在我們的肉體之中。那么在內(nèi)存中進(jìn)行垃圾回收的程序之“腎”,又是什么呢?

這是一段非常簡(jiǎn)單的 C 語(yǔ)言代碼。對(duì)于稍微有點(diǎn)基礎(chǔ)的人都知道在這段程序中,每一個(gè)變量所占用的內(nèi)存位置。

首先全局變量與靜態(tài)變量是放在數(shù)據(jù)段(RW 段,其中未初始化的放在 ZI 段,由程序啟動(dòng)的時(shí)候統(tǒng)一清內(nèi)存)比如:a,b,c;

局部變量放在??臻g中,比如:d,e;

同時(shí)還申請(qǐng)了一段存放于堆的內(nèi)存,但是代碼中并未使用 free 函數(shù)進(jìn)行釋放。

根據(jù)內(nèi)存的特性我們知道,對(duì)于 a,b,c 等數(shù)據(jù)段的變量,它們是常住內(nèi)存的,生命周期是永久的。對(duì)于棧里面的局部變量 d,e。它們的生命周期僅僅在“{}”之內(nèi),伴隨著棧操作的 push 以及 pop 指令,創(chuàng)建和消亡。

程序中當(dāng) e 消亡在花括號(hào)外后,在堆中申請(qǐng)的內(nèi)存就失去了指針對(duì)它的指向?qū)е铝?a class="article-link" target="_blank" href="/baike/1546935.html">內(nèi)存泄漏。如果是在簡(jiǎn)單的程序中,這樣的情況處理起來(lái)還是比較簡(jiǎn)單的,我們只要在程序后面采用 free 函數(shù)釋放內(nèi)存就可以。但是如果在擁有復(fù)雜的邏輯程序,這樣動(dòng)態(tài)申請(qǐng)的內(nèi)存就需要花不少心思去管理。這個(gè)就是為啥很多 java 之類的高級(jí)語(yǔ)言在制作教程的時(shí)候都會(huì)在與 C/C++比較時(shí)經(jīng)常強(qiáng)調(diào),java 沒(méi)有指針并且擁有垃圾自動(dòng)回收機(jī)制,會(huì)顯得更加安全,程序的健壯性更加容易得到保證。(當(dāng)然 C/C++也可以寫出健壯的程序,只是有些東西沒(méi)那么方便)。這種可以自動(dòng)幫助程序進(jìn)行內(nèi)存自動(dòng)垃圾回收的機(jī)制就是程序的“腎”了。

那么為啥 C/C++到現(xiàn)在都不支持垃圾自動(dòng)回收機(jī)制呢?我們可以從自動(dòng)垃圾回收機(jī)制的原理去尋到答案。首先說(shuō)一下自動(dòng)垃圾回收的判定算法,一般常用的是兩個(gè):

一、引用計(jì)數(shù)法。

所謂的引用計(jì)數(shù)法,顧名思義就是在內(nèi)存的描述結(jié)構(gòu)體內(nèi)部采用一個(gè)計(jì)數(shù)變量進(jìn)行計(jì)數(shù)。每當(dāng)有指針或者引用指向該內(nèi)存塊的時(shí)候,該內(nèi)存塊的描述結(jié)構(gòu)體內(nèi)部的計(jì)數(shù)器就遞增。當(dāng)指針或者引用被釋放或者改變的時(shí)候就遞減。當(dāng)內(nèi)存塊的計(jì)數(shù)遞減到 0 的時(shí)候,就可以釋放回收該內(nèi)存塊了。

引用計(jì)數(shù)法,應(yīng)該說(shuō)是最簡(jiǎn)單實(shí)現(xiàn)內(nèi)存可回收判定的算法。采用該算法實(shí)現(xiàn)自動(dòng)回收機(jī)制的典型的有 apple 開(kāi)發(fā)平臺(tái) Object-C 支持的 ARC 機(jī)制。這種自動(dòng)垃圾回收算法的實(shí)現(xiàn)有一個(gè)依賴和一個(gè)缺點(diǎn)。它的依賴就是需要編譯器自動(dòng)插入計(jì)數(shù)代碼。想 OC 在 xcode 平臺(tái)開(kāi)發(fā)程序,它的編譯環(huán)境會(huì)自動(dòng)地插入手動(dòng)進(jìn)行計(jì)數(shù)的函數(shù) retain,release 這樣的語(yǔ)句。所以這個(gè)實(shí)現(xiàn)自動(dòng)垃圾回收的本質(zhì)還是讓編譯器做手動(dòng)該做的事情而已。如果說(shuō) C 也需要實(shí)現(xiàn)類似的方式進(jìn)行自動(dòng)回收,那么就需要對(duì)編譯器的預(yù)處理過(guò)程進(jìn)行改造,并且在內(nèi)存申請(qǐng)和釋放的庫(kù)函數(shù)之上維護(hù)一個(gè)內(nèi)存的監(jiān)控結(jié)構(gòu),去給內(nèi)存塊做計(jì)數(shù)。

同時(shí)引用計(jì)數(shù)法法有一個(gè)非常大的缺點(diǎn),就是循環(huán)引用會(huì)導(dǎo)致內(nèi)存泄漏。如下代碼:

當(dāng)函數(shù)執(zhí)行完畢,a 與 b 相互引用。但是在棧中以及在數(shù)據(jù)段中已經(jīng)沒(méi)有指針可以訪問(wèn)到 a 與 b 的對(duì)象本身。也就是說(shuō)程序已經(jīng)失去了這兩塊內(nèi)存的訪問(wèn)權(quán),但是它們兩者又相互指向,導(dǎo)致內(nèi)存的計(jì)數(shù)無(wú)法歸零。所以一直不能釋放,導(dǎo)致了內(nèi)存泄漏,形成了垃圾。

二、可達(dá)性分析法。

可達(dá)性分析法,顧名思義就是分析內(nèi)存程序能否可以“達(dá)到”。也就是分析程序是否有失去對(duì)于內(nèi)存的訪問(wèn)權(quán)。程序在運(yùn)行狀態(tài)中,內(nèi)存時(shí)刻處于變化之中,猶如人體的血液流動(dòng)不止。但是不管在任何時(shí)刻,我們的程序一定可以訪問(wèn)的內(nèi)存大概有 2 個(gè)類別:

1、數(shù)據(jù)段,也就是全局變量與靜態(tài)變量。

2、??臻g中未釋放的變量也就是當(dāng)前入棧的動(dòng)態(tài)局部變量。

可達(dá)性分析法需要依賴于 Runtime,也就是運(yùn)行時(shí)環(huán)境,它們時(shí)刻監(jiān)控著上面兩個(gè)大類內(nèi)存中的指針變量或者引用,并且周期性地對(duì)這些指針或者引用的指向進(jìn)行遍歷,并且是遞歸逐級(jí)地往下遍歷。整體而言是在遍歷一個(gè)以這兩大類內(nèi)存中的指針變量和引用為入口的圖。只要能夠遍歷到的內(nèi)存塊就可以進(jìn)行可達(dá)性的標(biāo)志。當(dāng)程序進(jìn)入垃圾回收周期,它會(huì)遍歷已經(jīng)分配的所有內(nèi)存,如果訪問(wèn)到的內(nèi)存塊擁有可達(dá)性標(biāo)志,那么則跳過(guò)。如果沒(méi)有可達(dá)性標(biāo)志,則可以釋放回收。這樣就可以避免類似引用計(jì)數(shù)算相互引用導(dǎo)致不歸零,但是不可達(dá)卻又不釋放的問(wèn)題。如下圖,藍(lán)色內(nèi)存塊是會(huì)被回收的。

然而,可達(dá)性分析算法是需要依賴于運(yùn)行時(shí)環(huán)境的,也就是類似 java 那樣的虛擬機(jī)。所以目前 C/C++之類的語(yǔ)言還無(wú)法支持這種自動(dòng)垃圾回收的判定算法。

所以說(shuō)了那么多,我們對(duì)于這些程序語(yǔ)言的一個(gè)診斷是:

OC:apple 給它換了腎,但是腎不好,不過(guò)總體無(wú)礙。

java:腎很好啊。

C/C++:沒(méi)有腎的,需要程序員幫他做“腎透析”。

那么像 C/C++這么好的語(yǔ)言,我們能夠給它一個(gè)“腎”,讓它過(guò)上更加健康的生活嗎?

答案是有的。

相關(guān)推薦

登錄即可解鎖
  • 海量技術(shù)文章
  • 設(shè)計(jì)資源下載
  • 產(chǎn)業(yè)鏈客戶資源
  • 寫文章/發(fā)需求
立即登錄

最全電子漫畫(huà)收集達(dá)人,漫畫(huà)控必選!用文字和圖片帶你領(lǐng)略電子世界之美。 由曉宇哥哥操刀的芯片之家公眾號(hào),提供45萬(wàn)個(gè)Symbol和3D封裝庫(kù)免費(fèi)下載,定期分享軟硬件、物聯(lián)網(wǎng)類技術(shù)知識(shí)外,還精心整理大量參考設(shè)計(jì)和文檔資源,電路圖和源代碼資料供下載。 立即打開(kāi)“芯片之家 ”,感受電子與藝術(shù)的完美結(jié)合。