Java要拋棄祖宗的基業,Java程序員危險了!
?第11代Java國王坐在寶座上,俯視著臣民。
經過歷代國王的勵精圖治,他的Java帝國正處于巔峰狀態。
一群大臣看到新王登基,馬上上來拍馬屁。
“從后端到手機端,從手機端到大數據,帝國疆域無邊無際。” 線程大臣率先定了基調。
“Java是企業級應用無可撼動的霸主,生態環境極大豐富。Spring已經統治了后端開發?!?年邁的JVM大臣居然夸起Spring來!
“Java虛擬機性能強大,其他語言虛擬機都是玩具。” Spring大臣趕緊投桃報李。
......
都是一些聽過幾百遍的、老掉牙的東西。
國王聽得有些煩,揮手讓眾人退下。
他決定帶幾個保鏢,微服出宮,到外邊親自走一走,看一看。
1.微服私訪
走出都城大門,國王看到了一望無際的代碼田地。
烈日下,無數的Java碼農在這里辛苦勞作,CRUD的勞動號子響徹云霄。
國王走近一看,果然,碼農們用的工具都是SpringBoot和Spring Cloud,看來大臣所言不虛。
前面的大樹下,一個中年人開著小茶鋪,幾個碼農聚在那里,一邊休息喝水、一邊乘涼聊天。
國王悄悄走近。
中年人打著蒲扇,笑瞇瞇地說:諸位,你們知不知道,Java已經大禍臨頭,你們有可能要失業了。
一個戴著厚厚眼鏡的碼農笑得把茶都噴了出來:哈哈哈,危言聳聽,這怎么可能?
中年人慢悠悠地說:時代變了,原來的Java特別適合大規模的服務器端應用,尤其擅長時間高性能運行?,F在是云計算時代,微服務時代,有了容器,集群,服務可以隨時重啟,并且微服務越來越小,用什么語言都可以。
另一個花格子襯衫碼農說:那也可以用Java寫啊,SpringBoot挺好的啊,約定重于配置,內置服務器,一個jar包就跑起來。
其余幾個碼農紛紛附和,國王也暗自點頭。
中年人笑道:云端應用要求1. 鏡像小 2. 啟動速度快,即起即用。Java能做到嗎?
厚眼鏡碼農說:嗯,Java的docker鏡像動輒上G, 冷啟動實在太慢了,每次都得等半天!
花格子襯衫說:還有Spring啟動時用了太多的反射黑魔法,啟動速度更慢。
中年人說道:這就對了,我帶著小茶鋪游歷過Python王國、JavaScript王國,Go王國,人家那里就沒有這樣的問題,非常適合云端應用,你們不妨去看看啊。
一番話說得這幾個Java碼農動了心,開始竊竊私語,打探去那些王國的道路。
國王意識到這個中年人來者不善,給保鏢使了個顏色。
保鏢掀翻小茶鋪,扭起中年人就走,留下幾個碼農目瞪口呆。
2.三個計策
國王召來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嗎?
國王陷入沉思。
3.拋棄JVM
JVM大臣看到國王不說話,又描述了一遍Java程序的生命周期。
- JVM初始化
- 應用初始化
- 應用預熱
- 應用穩定
- 關閉
?
每個階段都有著重要使命,尤其是應用預熱的時候,會把Java字節碼編譯成本地代碼。
“如果拋棄JVM,前輩們所做的所有努力都不復存在!這會動搖我Java帝國的國本??!” JVM大臣伏地干嚎。
Java程序監控、擴展、jstat、jstack、jmap都用不了了。
調試的時候,也只能用復雜的GDB匯編調試,非常麻煩。
但是編譯成本地代碼,好處也非常明顯,沒有冷啟動問題,啟動即巔峰。
看到國王依然沒有反應,JVM大臣決定拋出殺手锏:
“陛下,我Java帝國之所以能稱雄世界,關鍵就是生態極其豐富,框架和類庫覆蓋了后端開發的所有方面。”
“而這些框架和類庫中在大量地使用反射,甚至用動態代理在運行時動態生成字節碼,換句話這些東西在編譯時根本無法確定,只有到運行時才能確定?!?/p>
“舉個例子,對于Class.forName("x.y.z")這樣的代碼,如何編譯時就把它變成成本地代碼?”
姜果然是老的辣,JVM大臣一下子就抓住了最關鍵的點,把皮球踢給了中年人。
沒想到中年人胸有成竹:“這非常簡單,在做靜態代碼分析的時候我會發現x.y.z是個需要被裝載的類,然后把它也編譯成本地代碼!”
“那如果這里不是個字符串的值,而是一個變量呢?Class.forName(someClassName)” JVM老頭得意地笑,他早就挖好了坑。
“那就沒辦法了,只好讓用戶在配置文件中告訴我們哪些類需要編譯成本地代碼了。”
“哈哈哈,說得輕巧,一個框架用了那么多反射,你讓用戶在配置文件中全部提前告訴你,怎么可能?”
中年人不甘示弱:“那我可以開發一個程序,讓用戶的程序運行一遍,我的程序監控用戶的程序哪些地方用了反射,然后自動生成配置文件!”
“程序那么多分支,你運行一遍就能找到所有用到反射的地方?”
JVM大臣轉向國王,斬釘截鐵地說:“陛下,此法斷不可行。”
“寡人覺得這其實就是不滿足封閉性原則。除了反射之外,還有動態代理,JNI,序列化等,當Java代碼使用這些特性的時候,靜態編譯就會遇到問題,需要想變通辦法,而變通辦法又無法覆蓋所有情況?!?/p>
國王果然是國王,高屋建瓴。
“陛下真是英明,一下子就上升到了理論層面,我等望塵莫及。” JVM趕緊拍馬屁。
4.編譯
“陛下,把這個散播謠言,鼓惑人心的家伙拉下去宰了吧!” Spring大臣提醒道。
“雖然Java的動態性無法完美滿足封閉性原則,但是靜態編譯確實是非常誘人,你說說,具體怎么做?!?國王不理Spring大臣,繼續詢問中年人。
“這個嘛,小民有個基本的思路,就是由用戶指定程序入口,嗯,相當于main函數,然后靜態編譯器從這里開始分析程序的可達范圍,把所有的可達的函數和一個小的運行時支持代碼編譯成native image?!?/p>
“可笑啊可笑,你難道忘記了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大臣不由得大笑起來。
“那肯定不行,我有個獨門絕技,叫‘指向性分析’,可以在不運行程序的情況下,找到一個類型變量在運行時的可能類型?!?nbsp; 中年人不慌不忙。
指向性分析?Spring大臣和JVM大臣再次對視,他們明白這位中年人不會多說了。
國王盯著這位中年人,問道:“你叫什么名字?”
“小民叫Graal?!?/p>
國王心里盤算起來。
云計算時代,容器技術的出現,write once, run anywhere已經不重要了。
相反,Java確實面臨著鏡像大,冷啟動慢的嚴峻挑戰。
把Java代碼編譯成本地代碼,要拋棄祖宗的基業,但可能是破局的關鍵。
自己作為新一代國王,堅決不能吃老本,更不能成為亡國之君,所有可能的方向都要嘗試。
想到此處,國王對中年人說:“好吧Graal,寡人已經明白你的意圖,現在給你一隊人馬,專門研究靜態編譯技術!Spring大臣你要密切配合!”
5.尾聲
幾個月后,中年人推出了一個新的虛擬機,叫做GraalVM,這個VM野心極大,不僅實現了把Java編譯成本地代碼,還支持JavaScript, Ruby, R,Python等語言。
?
雖然Spring大臣不太情愿,但是國王的圣旨不可違抗,他再次與時俱進,配合GraalVM推出了SpringNative ,把Spring應用編譯成了原生鏡像。
?
SpringNative啟動時間提升了50倍,并且啟動即巔峰,內存占用減少了5倍。
?
Java在云計算時代的危機暫時度過,未來它還會遇到什么挑戰呢?