我認識的python(1)

渣渣強發表於2018-07-02

第一篇部落格的由來

從接觸程式設計的時候,我就經常會看一些部落格內容。在接觸了很多大牛的分享之後,有一個想法一直在腦海中盤旋。那就是擁有一個自己的部落格,並開始寫一些自己對技術的心得體會。終於今天我下決心開始寫一些簡單的部落格內容,做自己的知識的沉澱。

解釋型語言和編譯型語言的區別

這個話題怎麼說呢,對於編譯型語言我其實不是很熟悉,雖然大學有初淺地學過一學期的c語言,但當時的我對技術還是處於迷茫的狀態,所以那時候對編譯型語言更是一頭 霧水。

不過前段時間剛好接觸一個專案,稍微看了點c++,結合自己的一些理解,如果有什麼理解上的錯誤,麻煩大家見諒,如果能夠指導更加感謝哈。
首先先說下編譯型語言吧,我理解的編譯型語言的執行過程是cpu通過載入經過編譯彙編連結後的指令集,並依次執行。cpu通過取址,分析,執行等過程最終完成一條條指令。

那麼我理解的解釋型語言是怎麼樣,首先大多數解釋型語言是執行在直譯器上,直譯器的責任其實雷同於cpu,只不過直譯器載入的是位元組碼(中間語言格式)而cpu是載入真正的指令集,直譯器執行的過程大致是通過讀取每一行的位元組碼,通過分析位元組碼的指令,然後轉化成對應的執行過程(像python的話,預設安裝的直譯器是cpython,他分析位元組碼後執行相應的c語言的程式碼)。當然直譯器的形態其實可以理解成一個應用程式,它的執行其實對應就是編譯型語言的執行過程,然後解釋型語言則在依賴於它進行執行。

那麼很明顯,如果實現同等的功能,解釋型語言的執行時間必然會比編譯型語言長(單純針對機器碼指令的數量來說),這也是編譯型針對解釋型的優勢點所在。所以一般針對cpu密集型應用,編譯型語言的時間開銷遠小於解釋型,但如果軟體的場景是io密集型,由於執行集執行時間遠小於io的讀取寫入時間,所以在這個場景下,兩個是會有差距,但不會很明顯。

但是編譯型語言最終需要執行的時候會轉化成二進位制的機器碼指令,這個指令的由來是原始碼通過預處理-編譯-彙編-連結最終變成真正的可執行檔案。 這裡新增一點自己的理解:

  1. 預處理這個過程其實是處理原始碼內所有預處理程式碼。比如include指令,它的執行過程會將它用到的標頭檔案內容替換到原始檔include相應的位置,像其中涉及到的函式定義最終對應組合語言是一個預留的程式碼段宣告,而像define則會將程式碼中所有的巨集定義轉化成相應的程式碼內容。
  2. 編譯過程是將預處理後的c語言程式碼轉化成彙編程式碼,大概就是產生程式碼段,堆疊段,bss段,資料段等。
  3. 彙編過程就是將上一步的彙編程式碼轉化成平臺相關的機器碼。
  4. 連結過程我簡單的理解就是說,但你的程式碼裡面用到很多模組的宣告函式,但假如你沒有把相應的程式碼拷貝到機器碼相應位置時候,這個程式碼其實是執行不起來,這個時候就必須填充預留的程式碼段的實際內容,然後更改機器碼中程式碼段的相對地址絕對地址的值,這樣這份檔案就可以真正的執行了。

所以這裡可以看到一份程式碼假如要在不同環境下執行,由於指令集的不同,必須重新進行編譯,沒辦法直接拷貝執行。而與之相反,解釋型語言有一個優點就是它具備跨平臺的能力,只要直譯器的解析執行這一層保持不變,底層去適配不同的環境,那麼在直譯器上執行的語言其實與底層的系統環境是隔離的。解釋型語言另外一個好處就是記憶體管理對使用者是透明的,因為像編譯型語言的話,如果它需要用到某些記憶體空間,需要進行malloc開闢記憶體空間,然後這個空間通過程式的虛擬空間對映到真實的物理頁(段頁式以及程式地址段PCB某個欄位的資料結構管理),然後當記憶體使用完畢,需要進行free釋放記憶體空間。而解釋型語言是由直譯器進行執行,所以可以猜測到解釋執行過程中的記憶體是不確定的,假如每次需要記憶體都去進行申請,效率明顯不高。這個時候預先分配一部分記憶體,然後對這部分記憶體進行管理。按大中小劃分記憶體,然後根據物件使用頻次進行記憶體管理的優化,物件的回收不釋放而是重新標記等待分配(當然記憶體不夠的時候會進行釋放)。

像python語言,預設的垃圾回收是引用計數,在物件引用變化期間會調整pyobject結構體的引用欄位,但欄位為0則進行物件回收,當然回收只是迴歸到空閒連結串列上。引用計數是一個比較簡單的垃圾回收演算法,但它沒辦法解決迴圈引用,針對這個問題,python引入了標記刪除回收演算法,在一定回收週期內,對不可達的物件進行回收(有一條連結串列維護引用使用中的物件),這個方法可以解決迴圈引用,但對於部分不經常被回收的物件,頻繁做標記回收顯得影響效能,所以這個時候涉及一個分代管理的演算法,本質上就是對不經常變動的物件延長回收時間,這樣就可以減少每次垃圾回收掃描的時間。

相關文章