小 u 身高 64 位,是內存世界 number 家族裏的一名浮點數變量。由於小 u 身體的二進制第一位是 0,因此按照 IEEE 754 標準,你們都把她當作女孩子來看待。她第 2 位到第 11 位的階碼並不夠大,使得她看起來小巧玲瓏;而她剩下的 52 個小數位十分精緻,這樣工做的時候和她打交道的變量舍入偏差都很小,因此你們都很喜歡她。git
小 u 天天的工做,是在內存世界裏和其餘的變量打交道,計算出有用的結果去造福人類世界。平時,在函數調用結束之後,小 u 就能夠下班回到她在源代碼裏的家了。她的工做壓力不大,不像那些身處 for 循環里名叫 i 呀 j 呀的變量那樣須要不停地加班連軸轉。而她的家也是自她出生以來就由人類世界裏的程序員編寫好的。別看那些程序員穿着邋遢,但對源代碼卻像對待本身的孩子同樣寵愛。小 u 在源代碼裏的家就是用一種名叫 JavaScript 的材料建起來的,不光有五光十色的編輯器主題來裝飾,還有嚴謹的分號和括號來保證家裏的結構的穩定和對稱,讓她頗有安全感。程序員
雖然有着可愛的外表、輕鬆的工做和舒心的家,但小 u 卻仍是有着本身的煩惱:她的家族出身決定了她不能有伴侶。github
在 JavaScript 這種材料所在的國度裏,number 家族隸屬於古老的基本類型家族。除了 number 以外,那些經典的數據結構,像字符串 string 和空值 null,都屬於基本類型家族。因爲簡單的基本類型很容易在代碼裏被解釋器推斷出來,因此他們的內存都是在一種死板的『棧』空間上預先分配好而不可變的。哪怕是和其餘 number 耳鬢廝磨地加加減減,也不能真正地在一塊兒。算法
而與基本類型家族相對的,則是時髦的引用類型家族。那些人類程序員青睞的所謂『面向對象編程』,說的就是這個家族。這個家族的成員複雜而多變,所以他們會被分配到廣袤的『堆』空間上,相互之間常常是你中有我,我中有你的狀態。比起註定孤獨一輩子的基本類型家族,有對象的引用類型家族無疑要滋潤得多。編程
小 u 有個不敢說出口的夢想,那就是努力成爲引用類型裏的一員。據說在遠方的 Java 國度,有一條叫作『自動裝箱』的法律可以讓本身的家族看起來像引用類型家族同樣,那樣她也許就能夠再也不孤獨了。瀏覽器
夢想歸夢想,她對本身的生活其實仍是挺滿意的。在內存世界習慣以後,工做和生活的平衡是許多人類世界的程序員一生都達不到的。這樣的生活一直繼續着,直到有一天……安全
那天像往常同樣,小 u 從源代碼的家裏出發,經過詞法分析門後,搭上了語法分析班車的軌道。班車上 JIT 的標識表明着 Just-In-Time,就好像人類世界中『JR 新幹線』和『和諧號』那樣,是高效、快捷的象徵。數據結構
班車迅速地把小 u 載到了語法樹軌道上的葉子節點站臺。走下班車,站臺上有一張 64 位尺寸的長椅。她坐上椅子閉上眼,等待着解釋器對她的掃描和調用。閉包
『希望此次不要趕上粗俗的 null 值……』小 u 默唸着,眼前一陣電光閃動,隨着內存世界底層無數晶體管狀態的改變,解釋器如期讀取了小 u 的值。在這條原子性的指令裏,小 u 須要讓解釋器徹底地控制本身,她歷來不知道從電光閃動到再一次睜開眼睛之間,內存世界裏發生了什麼。編程語言
『嗯……』她如期醒來了,照理說她在醒來時仍是會身處一樣的站臺位置,等待回程的語法樹班車接她回家。
眼前仍是一樣的景象,不對,好像又有哪裏不同——站臺的結構和佈置彷佛和以前別無二致,只是少了同樣東西:軌道上空空蕩蕩,沒有等待她的班車,更沒有別人。難道……誤點了?她打內心不相信這樣低級的錯誤會出現頻率精準的內存世界裏。不過班車沒來就是沒來,她只好在站臺上繼續等待。
時間一赫茲一赫茲地通過,小 u 心裏的不安和焦慮也在慢慢增長:到底發生了什麼?班車是忘記我了嗎?仍是說提早開走了?女孩子一我的在外呆這麼久是很不安全的,可是做爲嚴謹的變量,獨自行動更是內存世界裏的大忌。『仍是……再等等吧……』小 u 有些絕望地想。
班車仍是沒有到。
『不行了,我必須回源碼裏去啊!』等待終於讓小 u 的情緒激動起來了,她開始在站臺上尋找其它的出口,想要找到回家的路。軌道不能跳下去,但站臺的兩頭有個紅色的 Exit 標識,那裏看起來是個能夠通行的出口。不過現代編程語言國度裏的變量通常歷來都不這麼走,由於手動的內存操做很危險。
小 u 打量四周,當心翼翼地推開了回程那頭 Exit 下鏽跡斑斑的門。謝天謝地,這裏是有路的,而且看起來不是那麼危險。她走過一段狹長的走道,走道里每隔固定的長度就會亮着一個小小的指示燈,看起來是內存地址空間的下標標識。終於,她看到了出口:一扇形狀相同的 Exit 門。小 u 火燒眉毛地推開門,想看看本身有沒有更接近家一點。
眼前的景象讓她詫異:如出一轍的軌道、如出一轍的長椅、如出一轍的站臺、如出一轍的 Exit,就好像本身根本沒有移動過同樣!
難道我走錯路了嗎?這不可能呀!小 u 對方向這樣非 0 即 1 的狀態有着絕對的自信,她知道她不會走錯的。也許這段地址空間裏的內容都是這樣吧?沒事的,再走走就不同了吧。因而,天真的她開始了漫長的步行,然而讓她一點點喪失信心的是,每個 Exit 都通向一樣的站臺,毫無區別,甚至連鏽跡都是同樣的。『有人嗎!』她開始呼救,儘管看起來有些徒勞。又這樣支撐了一會,她終於感受要放棄了,疲憊地坐在一個站臺的長椅上聽天由命。
……
『你迷路了嗎?』
耳邊一個聲音響起,她驟然驚醒,蜷縮起來打量着聲音的來源。這也是個 number 家族的浮點數,從第一位 1 來看是個男孩子,有着高她一個頭的階碼和粗糙的小數位。
『你是誰……這又是哪裏?』
『我是小 s,這裏是閉包的堆空間。』
『閉包……堆?』
『是啊,咱們家族的變量平時都是分配在棧上,每次調用的生命週期很快就能結束了。可是如今不知道在哪一個函數裏還有着對咱們的引用,因此咱們還無法被清除掉……』
『等等!生命週期是什麼東西啊?難道個人生命還會結束嗎?』
看到小 u 迷茫的樣子,小 s 顯得很吃驚:『難道你不知道嗎?咱們變量的生命一共有三重死亡呀。第一重,發生在咱們離開做用域的時候,好比一個函數返回之後。這時候在上下文裏就找不到咱們了,咱們這一重生命週期結束,可是不會被立刻銷燬掉。第二重,發生在內存中再也不有引用咱們的地方,解釋器進行垃圾收集的時候。這時候咱們完全離開內存世界,回到源代碼裏。第三重,是人類世界裏的程序員把咱們的定義代碼刪除的時候,那時候纔是最終的死亡。』
『那……難道我每次回到源碼家裏的時候,都……』
『是的,會發生前兩重的死亡。可是隻要源碼沒有被刪除,咱們就仍然存在於世界上。而且,前兩重死亡發生得很是快,咱們根本感受不到。』
『但是,這樣從新回到源碼裏的我仍是我嗎?』
『別問我這麼深奧的問題啊……不過你要這麼說的話,一我的尚未辦法重複踏進兩次河流呢!』
『噢……好像是這樣……但是你剛纔說的什麼堆……』小 u 看起來仍是很困惑。
『哦哦,你說這個啊!咱們雖然是基本類型,但也不必定分配在棧上的。有可能引用類型會裏動態地用到咱們,這時候咱們也有可能被分配在堆上呀。』小 s 仍是在一本正經地說教。
閉包…引用類型…堆…小 u 恍然大悟,原來本身所在的空間,已經不是以前那個可以及時把她釋放到回程班車上的棧空間了。因爲某個函數或者引用類型此刻還有若干指向本身的地方,所以她被分配在了動態的堆空間上——這不就是她一直但願的嗎!不過,因爲解釋器對堆空間的自動內存回收尚未運行,所以她如今只能和小 s 在這片空間裏遊蕩,就好像被詛咒了同樣。
『因此,咱們能一塊兒回去嗎?』
『原本咱們確定能夠一塊兒回去的,可感受好奇怪,照理說解釋器早該自動把咱們這一帶的內存都回收了,怎麼到如今仍是什麼都沒發生……』小 s 雖然看起來博聞強識,不過對於眼前的狀況仍是有些困惑。
『會不會這一帶還有別人在使用……』小 u 的判斷力好像恢復了。
『若是按正常的內存分配,到如今應該早就自動回收了呀。除非內存泄漏……啊!』小 s 好像被本身嚇到了。
『那又是什麼啊?』
『說來話長了……這麼說吧,內存世界裏一些制度比較老的國家,是讓人類世界的程序員手動把咱們釋放掉的。這個規矩常常漏掉一些變量,給咱們帶來了很大的痛苦。咱們 JavaScript 這邊倒好一點,可讓解釋器幫咱們自動回收內存……』
『欸?那不是很好嗎?』
『哎呀,自動回收的代碼也是那幫不靠譜的程序員寫的,該有的問題仍是會有的呀。好比那個蹩腳的 IE 瀏覽器,出現循環引用的時候就會出問題……啊對了!怪不得咱們出不去了!估計咱們是被困在 IE 裏了!』
『循環…引用…?』
『這個簡單說是這樣的:假如咱們不是浮點數,是引用類型的對象的話,那麼只要 u
這個對象有個屬性指向我,而個人一個屬性指向 u
,這個你中有我我中有你的狀況就是循環引用了啊。』
小 u 的臉突然紅了。不過遲鈍的小 s 仍是口若懸河:『現代的瀏覽器作內存回收的算法廣泛是標記清除算法,這個算法沒有循環引用問題。可是早期 IE 用了一個叫引用計數的算法,這個算法在剛纔那種狀況的時候引用計數就不會清零,這樣內存就不會被解釋器收集了……』
『啊……因此咱們回不去了嗎?』
小 u 的疑問把小 s 從知識的海洋里拉了出來。如今,他們終於明白了現狀:兩個孤獨的基本類型變量沒有辦法被自動回收,只要用戶不停機,他們就會被永遠困在這裏,就像盜夢空間裏那樣。而且數學上已經證實,停機問題是不可解的。兩人間長長的沉默降臨了。
終於,小 s 打破了沉默:『其實……我想到了一個方法,能夠試試。』
『嗯嗯,是什麼啊?』
『我在的代碼段應該還會執行,在那個時候,我想辦法觸發一個異常,讓程序掛掉。』
『但是咱們都好好地在這裏了呀,已是正確的代碼怎麼會報錯呢?』
小 s 苦笑了一下:『看來你對 JavaScript 的奇葩一無所知啊。聽說當初國父 Brendan Eich 制定基本國策的時候只用了一個週末,因此這門語言處處是暗坑,就算看起來結構工整規範的代碼,那些人類程序員也常常寫得亂七八糟。』
『因此,怎麼……』
『好比說,雖然我是浮點數,可是其實由於我是在 if
裏聲明的,因此只要我願意,我就能用一個叫作變量提高的設計缺陷,把我本身臨時變成 undefined
。』
『那樣的話,類型就錯了呀。』
小 s 又自信了起來:『對,只要我抓住那次機會,把這時候的我和其餘變量作一次運算,就能把返回的類型從浮點數變成危險的 NaN
了。這樣後面用到結果的地方確定都不對,就算程序不崩潰,人類世界的用戶或者程序員也能發現這個問題了。』
『他們發現了之後又能怎麼樣呢?』
『會重構掉我這段代碼,而後你也能夠回去了。』
『這樣的話,一旦你的代碼消失了,豈不是……』
『沒事,很高興認識你……』小 s 已經慢慢走到了站臺一側的邊緣了,那裏有一個左花括號擋住了他。他看準花括號前的地磚,使勁地踩了下去。一瞬間,變量提高就把他帶出了做用域。沒有過多少赫茲的時間,站臺的地面下就開始搖晃,傳來了燃燒着的報錯對象從地下一層層拋出調用棧的聲音。隨着砰的一聲巨響,報錯對象撕裂了地面——這也是小 u 最後記得的場景了。
在記憶中的下一個鏡頭,她已經在回程的語法樹班車上了。回到源代碼裏,而後等待着後面的調用,一切又彷佛從新變得那麼天然,好像什麼都沒有發生過。固然了,她所在的源代碼模塊裏沒有一個叫作 s
的變量,也許是在那個異常拋出以後就被人類加班加點地 hotfix 重構掉了吧。
幾個版本以後,小 u 在一次代碼優化中終於如願以償地成爲了引用類型的屬性。初來乍到的這個新源碼家庭的時候,她看到這個 class 的屬性裏,來了一個熟悉的新成員。
『啊,u』
『啊,s』
異口同聲地,他們說出了對方的名字。
END
這是做者博客的第一篇小說,也是一篇開源的小說,歡迎在 Github 上提出意見和建議😀