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