JVM和Python解釋器的硬碟夜話

關注頭條號,私信回覆資料會有意外驚喜呦………………最後一張照片有資料呦。

JVM和Python解釋器的硬盤夜話

這個電腦的主人是個程序員,他相繼學習了C, Java ,Python, Go, 但是似乎停留在Hello World的水平。

隨著hello.c, HelloWorld.java , Hello.py等文件被刪除,曾經熱鬧非凡的硬盤夜話也冷清了起來.....

(碼農翻身友情提示:參見文末的硬盤夜話系列文章)

JVM先生

JVM先生髮覺有點不太對勁,原來那些圍著自己獻殷勤的Java文件都不見了。

茫然四顧,也找不到一個可以執行的class文件, JVM先生覺得非常孤獨。

到隔壁目錄逛逛吧,說不定還有點新發現。

果然,隔壁目錄是正在發呆的Python解釋器,JVM先生曾經見主人用它執行過一次Hello.py。

當Python明白JVM先生的處境,不由得幸災樂禍起來: “看來你活不久了,傳說中可怕的卸載很快就會來找你了。”

“怎麼可能?你才活不久! 可能你還不知道吧,Hello.py也去回收站享清福了,你現在和我一樣,都是孤家寡人!” JVM先生馬上反駁, “再說了,主人怎麼可能卸載我? Java可是世界上使用者最多的語言。”

“你沒看到主人穿的T恤上寫的字嗎? 人生苦短,我用Python,這已經充分說明一切了。” Python解釋器補了一刀。

“得意什麼? 你不就是個小小的解釋器嗎? 怎麼能和我這性能卓越的虛擬機相比?”

“解釋器? 你居然當我是解釋器? 我明明是虛擬機好不好?別以為只有你有字節碼,我也有。” Python解釋器急忙澄清自己的身份。

“那你還不是解釋執行的?” JVM先生有點底氣不足。

“你是隻知其一,不知其二,我看起來是直接解釋執行的,實際上我在背後把Python文件做了編譯,也形成了字節碼。”

說著,Python給出了一段自己的字節碼

LOAD_FAST 0 (x)

LOAD_FAST 1 (y)

BINARY_ADD

LOAD_CONST 1 (10)

BINARY_MULTIPLY

RETURN_VALUE

經驗老道的JVM先生一眼就看出來,這是基於棧的虛擬機!

你看它先把x, y 兩個變量從某個地方給取出來,壓入棧中, 然後彈出,做加法運算,把結果也壓入棧中。

接下來把常量10 壓入棧中,把上個結果(x+y) 和10 進行相乘, 最後返回。

其實這段代碼表達的就是 (x+y)*10 ! 和自己的JVM字節碼真是非常像!

(碼農翻身友情提示: 在《我是一個Java Class》中對基於棧的操作有漫畫描述)

雖然胸有激雷, 但JVM壓抑著努力做到面如平湖, 他淡淡地說:這不就是 (x+y)*10 嘛!

垃圾回收

“哈哈,我就知道老兄你一眼就能看透, 除此之外,我也有垃圾回收呢,主人只需要把對象創建起來,根本不用管什麼時候把對象佔據的空間和釋放掉。” Python再次拋出炸彈。

“垃圾回收?你是怎麼做垃圾回收的? ” JVM先生一下子興奮起來,這可是他最厲害的領域之一,Python竟然敢班門弄斧!

“我主要使用簡單明瞭的引用計數法。” Python很得意。

所謂引用計數法就是給每個對象都增加一個“引用計數”的字段,每次有新的變量指向了對象A,A的引用計數就會加一,變量指向了別的對象,A的引用計數就是減一,當引用計數為0 ,就意味著對象A可以被回收了。

1a1 = ClassA() # a1指向對象(簡稱對象A)的引用計數為 1

2a2 = a1 # a1,a2 指向同一個對象,對象A引用計數為 2

3a1 = ClassB() # a1 指向新的對象, 對象A的引用計數變為1

JVM和Python解釋器的硬盤夜話

“看起來簡單,實際上一點都不簡單,每次遇到變量的賦值操作的時候,你都得把增加新對象的引用計數,還得減少老對象的引用計數,更要命的是循環引用問題, 你怎麼解決?” JVM先生問道。

1a = ClassA() # 對象A的引用計數為1

2b = ClassB() # 對象B的引用計數為1

3a.t = b # 對象B的引用計數為2

4b.t = a # 對象A的引用計數為2

5del a # 對象A還在被b所引用,引用計數還是為1,無法刪除

6del b # 對象B還在被a所引用,引用計數還是為1,無法刪除

JVM和Python解釋器的硬盤夜話

Python嘿嘿一笑:“我不是說了嗎,我主要是引用計數,我還有標記-清除,分代回收等算法作為輔助呢,從一個根集合開始,查找還被引用的,需要存活的對象...... 想來你是十分熟悉了。”

JVM先生當然很熟悉,想想自己的年輕代(裡邊還要劃分成eden,survivor),年老代,Minor GC,Full GC,各種各樣的垃圾收集器Serial、PraNew、Parallel Scavenge,Serial Old、Parallel Old、CMS,各種各樣的參數調優,經常把新手搞得眼花繚亂,又興奮又迷茫。

沒想到這小子也有一套標記-清除,分代回收,看來在理論基礎上就難於壓倒他了。

“可是,網上討論Java 垃圾回收的文章鋪天蓋地,為什麼很少人討論Python垃圾回收的參數,調優啊?是不是你做得不怎麼樣啊?” JVM先生很疑惑。

“嘿嘿,那是因為我就不給Python程序員提供那些煩人的調優選項,你只要用就行了,難道你寫個Python腳本還要關注垃圾回收嗎? 沒必要! 人生苦短,我用Python,很有道理!”

GIL

“既然你用引用計數,怎麼處理多個線程同時修改一個對象的引用計數問題? 如果引用計數被錯誤地修改,

很可能會導致一個對象一直不被回收,或者回收了一個不能被回收對象。 難道你在每個對象上都加了一把鎖? 只讓一個線程進入修改?” JVM 的思考頗有深度。

“嘿嘿,我沒有在每個對象上都加鎖,每次訪問都加鎖、解鎖,開銷太大! 並且還很容易引發死鎖相反, 我只設置了一把鎖,Global Interpreter Lock ,簡稱GIL, 這把超級大鎖只允許一個線程獲得Python解釋器的控制權, 簡單來說,同一時刻,只有一個線程能運行!”

“同一時刻,只有一個線程能運行? ” JVM簡直不敢相信,這絕對顛覆了自己的世界觀和人生觀。

用戶寫了多線程的程序,如果CPU有多核,只有一個線程執行,怎麼利用多核? 是為了實現“一核有難,多核圍觀”嗎?

線程切換的時候還得釋放GIL,競爭GIL,多線程可能跑得比單線程都慢了! 要多線程有什麼用?

“其實也沒什麼大不了的,老兄你也知道,這程序的瓶頸啊,它不在CPU, 而在於IO, 就是用戶的輸入,數據庫的查許,網絡的訪問,

線程等到有IO操作的時候,放棄GIL這個超級大鎖,讓別的線程去執行就是了。”

JVM和Python解釋器的硬盤夜話

“那要是有個CPU密集型的線程在執行,根本沒有I/O, 一直霸佔著GIL不放,那該怎麼辦? ” JVM先生問道。

“放心吧,我肯定不能讓他霸佔著CPU不放,我也得給別的線程一個機會運行。 具體的做法也很簡單,每當線程執行了100 ticks, 就需要釋放這個GIL。”

“tick ? 是時鐘週期嗎?”

“不是時鐘週期,是和我的字節碼相關的,一個tick映射到一條或多條字節碼。”

“當線程A執行了100個ticks以後,你就讓他放棄GIL,然後具體怎麼處理?” JVM先生刨根問底。

“然後我就發個信號給操作系統老大嘍,讓他去調度那些因為沒有獲得GIL鎖而掛起的線程,大家去競爭這把鎖,當然線程A也會參與競爭,大家都站在同一個起跑線上,誰獲得了GIL, 誰就可以執行了。 ”

JVM和Python解釋器的硬盤夜話

JVM覺得Python的這種作法實在是古怪,操作系統老大本來有一套自己的線程調度的策略,現在你為了讓線程釋放GIL, 又來搞個什麼ticks, 把簡單的東西給變複雜了啊。

JVM先生很快想到另外一個問題: “線程A也會參與競爭?! 那要是在多核情況下,被分配到其他核的線程由於需要等待信號,喚醒以後才能競爭,線程A會不會經常搶先,‘打壓’別的線程,讓它們難以抬頭,難以運行? ”

JVM和Python解釋器的硬盤夜話

Python不由得佩服JVM,它在這方面知識儲備真厲害,一下子就抓住了關鍵的小尾巴。他尷尬地笑了笑: “嗯,有這個可能。 ”

JVM從打心底鄙視這種GIL的全局鎖,太不講人性了。

“如果真想利用多核的特性,還想避開GIL, Python專家建議,還是用多進程吧! ” Python無奈地說道。

“多進程? 你要知道每個進程都是獨立的,數據共享起來比線程要麻煩得多! 程序不經過大改動是不行的。 你們怎麼不把這個不講人性的GIL給去掉啊??”

“哎呀,不好改啊,歷史遺留問題了, 我們Python誕生於上世紀90年代初,比你Java 還早。 Python的設計目標就是易於使用,易於擴展,很多用C語言寫的擴展庫被開發出來,由於有GIL, 這些擴展庫都不必考慮線程安全問題,很容易被集成進來。”

看來存在就是合理的,C的擴展庫極大地豐富了Python的功能,促進了Python的發展和使用。

但是隨之多核的出現和流行,GIL慢慢地不合時宜了。關鍵是現在想去修改也很難了。

“那你們有沒有計劃,什麼時候把GIL給幹掉?”

“我覺得等到Python 3000也許有戲。” Python開玩笑,他還挺樂觀。

JVM先生突然想到一件事情:“我聽說你們Python語言在我的JVM上也有實現,叫做什麼Jython,它有GIL的限制嗎?”

“Jython啊,他在底層都被編譯成你的Java字節碼了,在你的虛擬中運行,是沒有GIL的。”

“哼哼,還是我的平臺厲害吧!” JVM先生很得意。

尾聲

兩人正聊得熱火朝天, 突然看到主人回到電腦前,拿起鼠標,敲起鍵盤,不知道要做什麼事情。

兩人非常緊張,惴惴不安地迎接最終的審判: 卸載。

可怕的卸載並沒有來臨, 相反,電腦裡入住了兩個IDE, 一個是IntelliJ IDEA, 還有一個是PyCharm,兩人不由得歡呼起來: 看來主人並不打算拋棄我們,而是要用IDE做點大項目了!

JVM和Python解釋器的硬盤夜話


分享到:


相關文章: