Java要拋棄祖宗的基業,Java程式設計師危險了?
第11代Java國王坐在寶座上,俯視著臣民。
經過歷代國王的勵精圖治,他的Java帝國正處於巔峰狀態。
一群大臣看到新王登基,馬上上來拍馬屁。
“從後端到手機端,從手機端到大資料,帝國疆域無邊無際。” 執行緒大臣率先定了基調。
“Java是企業級應用無可撼動的霸主,生態環境極大豐富。Spring已經統治了後端開發。” 年邁的JVM大臣居然誇起Spring來!
“Java虛擬機器效能強大,其他語言虛擬機器都是玩具。” Spring大臣趕緊投桃報李。
……
都是一些聽過幾百遍的、老掉牙的東西。
國王聽得有些煩,揮手讓眾人退下。
他決定帶幾個保鏢,微服出宮,到外邊親自走一走,看一看。
微服私訪
走出都城大門,國王看到了一望無際的程式碼田地。
烈日下,無數的Java碼農在這裡辛苦勞作,CRUD的勞動號子響徹雲霄。
國王走近一看,果然,碼農們用的工具都是SpringBoot和Spring Cloud,看來大臣所言不虛。
前面的大樹下,一箇中年人開著小茶鋪,幾個碼農聚在那裡,一邊休息喝水、一邊乘涼聊天。
國王悄悄走近。
中年人打著蒲扇,笑眯眯地說:諸位,你們知不知道,
Java已經大禍臨頭,你們有可能要失業了
。
一個戴著厚厚眼鏡的碼農笑得把茶都噴了出來:哈哈哈,危言聳聽,這怎麼可能?
中年人慢悠悠地說:時代變了,
原來的Java特別適合大規模的伺服器端應用,尤其擅長時間高效能執行
。現在是雲計算時代,微服務時代,有了容器,叢集,服務可以隨時重啟,並且微服務越來越小,用什麼語言都可以。
另一個花格子襯衫碼農說:那也可以用Java寫啊,SpringBoot挺好的啊,約定重於配置,內建伺服器,一個jar包就跑起來。
其餘幾個碼農紛紛附和,國王也暗自點頭。
中年人笑道:雲端應用要求
1。 映象小 2。 啟動速度快,即起即用
。Java能做到嗎?
厚眼鏡碼農說:嗯,
Java的docker映象動輒上G, 冷啟動實在太慢了,每次都得等半天
!
花格子襯衫說:還有
Spring啟動時用了太多的反射黑魔法,啟動速度更慢
。
中年人說道:這就對了,我帶著小茶鋪遊歷過Python王國、JavaScript王國,Go王國,人家那裡就沒有這樣的問題,非常適合雲端應用,你們不妨去看看啊。
一番話說得這幾個Java碼農動了心,開始竊竊私語,打探去那些王國的道路。
國王意識到這個中年人來者不善,給保鏢使了個顏色。
保鏢掀翻小茶鋪,扭起中年人就走,留下幾個碼農目瞪口呆。
三個計策
國王召來Spring大臣和JVM大臣,一起審問這個中年人。
國王:你是何人,為什麼在那裡危言聳聽、鼓惑我朝年輕人?
中年人:小民說的都是事實啊,陛下,您可能被矇蔽了,外界正在發生翻天覆地的變化啊,Java如果不與時俱進,岌岌可危啊。
Spring大臣和JVM大臣互相看了一眼,意味深長。
國王倒不在意,問道:你有什麼建議?
中年人:小民有一個上策、中策和下策,陛下想先聽哪一個?
國王:哦?三個計策?先說說下策。
中年人:下策自然是保留現狀不變。
Spring大臣:相當於沒說,中策呢?
中年人:中策就是改Spring,Spring應用在啟動時會掃描程式碼中的bean,然後用反射的方式註冊bean,這種做法的耗時與應用的程式碼量成正比,所以啟動效能會很差。
如果在編譯時把反射轉化為直接呼叫的類,將會大幅提升應用的啟動速度
。我的研究顯示,這種辦法至少可以將成本降低50%,並且民間已經出現了一個叫做Micronaut的框架,它已經實現了
編譯期的依賴注入
!
Spring大臣一聽這傢伙要把自己幹掉,大驚失色,趕緊跪倒。
他先回顧了祖上如何用SpringMVC乾死Struts的英勇事蹟,又不動聲色地提起自己如何與時俱進,用SpringBoot、Spring Cloud,Spring WebFlux在微服務時代和反應式程式設計時代勇立潮頭。希望Java國王能念起舊情。
國王眼珠一轉,看了一眼JVM大臣:好吧,也許這種辦法能提升Spring應用的啟動速度,但是據我所知
JVM的啟動速度也很慢
,這又該怎麼辦?
中年人:這就是我要說的上策了,
拋棄JVM,把Java程式編譯成原生代碼來執行!
“大膽!你這是要革命,要謀反!” JVM大臣忍不住了。
“陛下,這等狂悖之徒,拉下去問斬吧!” Spring大臣也立刻拱火。
國王心裡很清楚,二十多年了,Java帝國最厲害的無過於位元組碼和JVM,如今ZGC垃圾回收器停頓時間不超過10ms,停頓時間還不會隨著堆的增大而增大,JVM的JIT也爐火純青,在執行時找到最熱點的程式碼,編譯成本地二進位制執行,效率直逼C語言!
相比之下,JavaScript和Python虛擬機器能叫虛擬機器嗎?玩具而已!它們怎麼不強調自己的停頓時長?
不過這個計策倒是非常大膽,雲計算時代,真的需要JVM嗎?
國王陷入沉思。
拋棄JVM
JVM大臣看到國王不說話,又描述了一遍Java程式的生命週期。
1。JVM初始化
2。 應用初始化
3。應用預熱
4。應用穩定
5。關閉
每個階段都有著重要使命,尤其是應用預熱的時候,會把Java位元組碼編譯成原生代碼。
“如果拋棄JVM,前輩們所做的所有努力都不復存在!這會動搖我Java帝國的國本啊!” JVM大臣伏地乾嚎。
Java程式監控、擴充套件、jstat、jstack、jmap都用不了了。
除錯的時候,也只能用複雜的GDB彙編除錯,非常麻煩。
但是編譯成原生代碼,好處也非常明顯,沒有冷啟動問題,啟動即巔峰。
看到國王依然沒有反應,JVM大臣決定丟擲殺手鐧:
“陛下,
我Java帝國之所以能稱雄世界,關鍵就是生態極其豐富,框架和類庫覆蓋了後端開發的所有方面。
”
“而這些
框架和類庫中在大量地使用反射,甚至用動態代理在執行時動態生成位元組碼
,換句話這些東西在編譯時根本無法確定,只有到執行時才能確定。”
“舉個例子,對於Class。forName(“x。y。z”)這樣的程式碼,如何編譯時就把它變成成原生代碼?”
姜果然是老的辣,JVM大臣一下子就抓住了最關鍵的點,把皮球踢給了中年人。
沒想到中年人胸有成竹:“這非常簡單,在做靜態程式碼分析的時候我會發現x。y。z是個需要被裝載的類,然後把它也編譯成原生代碼!”
“那如果這裡不是個字串的值,而是一個變數呢?Class。forName(
someClassName
)” JVM老頭得意地笑,他早就挖好了坑。
“那就沒辦法了,只好讓使用者在配置檔案中告訴我們哪些類需要編譯成原生代碼了。”
“哈哈哈,說得輕巧,一個框架用了那麼多反射,你讓使用者在配置檔案中全部提前告訴你,怎麼可能?”
中年人不甘示弱:“那我可以開發一個程式,讓使用者的程式執行一遍,我的程式監控使用者的程式哪些地方用了反射,然後自動生成配置檔案!”
“程式那麼多分支,你執行一遍就能找到所有用到反射的地方?”
JVM大臣轉向國王,斬釘截鐵地說:“陛下,此法斷不可行。”
“寡人覺得這其實就是不滿足
封閉性原則
。除了反射之外,還有動態代理,JNI,序列化等,當Java程式碼使用這些特性的時候,靜態編譯就會遇到問題,需要想變通辦法,而變通辦法又無法覆蓋所有情況。”
國王果然是國王,高屋建瓴。
“陛下真是英明,一下子就上升到了理論層面,我等望塵莫及。” JVM趕緊拍馬屁。
編譯
“陛下,把這個散播謠言,鼓惑人心的傢伙拉下去宰了吧!” Spring大臣提醒道。
“雖然Java的動態性無法完美滿足封閉性原則,但是靜態編譯確實是非常誘人,你說說,具體怎麼做。” 國王不理Spring大臣,繼續詢問中年人。
“這個嘛,小民有個基本的思路,就是由
使用者指定程式入口,嗯,相當於main函式,然後靜態編譯器從這裡開始分析程式的可達範圍,把所有的可達的函式和一個小的執行時支援程式碼編譯成native image。
”
“可笑啊可笑,你難道忘記了Java是個面向物件的語言,多型無處不在?” JVM大臣諷刺。
“我給你舉個例子,看看你怎麼做靜態分析。”
void process(List employees){ int size = employees。size(); ……}
“這個List是JDK的一個介面,JDK有很多實現類(ArrayList,LinkedList,Vector等),我們的專案也有很多自定義的List實現類,employees的實際型別只能在執行時確定,你的靜態分析如何確定呢?”
“你不會把List的所有實現類都給編譯成二進位制程式碼吧?” Spring大臣馬上添油加醋。
“如果是這樣的函式
void process(Object o)
,Object是所有型別的根,難道你要編譯所有的類?哈哈哈!” JVM大臣不由得大笑起來。
“那肯定不行,我有個獨門絕技,叫‘
指向性分析
’,可以在不執行程式的情況下,找到一個型別變數在執行時的可能型別。” 中年人不慌不忙。
指向性分析?Spring大臣和JVM大臣再次對視,他們明白這位中年人不會多說了。
國王盯著這位中年人,問道:“你叫什麼名字?”
“小民叫Graal。”
國王心裡盤算起來。
雲計算時代,容器技術的出現,write once, run anywhere已經不重要了。
相反,Java確實面臨著映象大,冷啟動慢的嚴峻挑戰。
把Java程式碼編譯成原生代碼,要拋棄祖宗的基業,但可能是破局的關鍵。
自己作為新一代國王,堅決不能吃老本,更不能成為亡國之君,所有可能的方向都要嘗試。
想到此處,國王對中年人說:“好吧Graal,寡人已經明白你的意圖,現在給你一隊人馬,專門研究靜態編譯技術!Spring大臣你要密切配合!”
尾聲
幾個月後,中年人推出了一個新的虛擬機器,叫做GraalVM,這個VM野心極大,不僅實現了把Java編譯成原生代碼,還支援JavaScript, Ruby, R,Python等語言。
雖然Spring大臣不太情願,但是國王的聖旨不可違抗,他再次與時俱進,配合GraalVM推出了SpringNative ,把Spring應用編譯成了原生映象。
SpringNative啟動時間提升了50倍,並且啟動即巔峰,記憶體佔用減少了5倍。
Java在雲計算時代的危機暫時度過,未來它還會遇到什麼挑戰呢?