現(xiàn)在的 Java 還是平臺無關(guān)的嗎?
答案,真不一定!
雖然 Java 在誕生之初,設(shè)計的主要目標就是跨平臺,并且也做了很多事情來讓這個成為可能,也是很多年來Java的一個重要優(yōu)勢。但隨著技術(shù)的發(fā)展和應(yīng)用場景的變化,Java的跨平臺特性并不是在所有情況下都是絕對必要的。
Java 是如何實現(xiàn)跨平臺的?
平臺無關(guān)性就是一種語言在計算機上的運行不受平臺的約束,一次編譯,到處執(zhí)行(Write Once, Run Anywhere)。也就是說,用Java創(chuàng)建的可執(zhí)行二進制程序,能夠不加改變的運行于多個平臺。
Java通過JVM(Java虛擬機)實現(xiàn)了平臺無關(guān)性。JVM是一個平臺相關(guān)的軟件,它為Java程序提供了一個統(tǒng)一的運行環(huán)境。只要設(shè)備上安裝了相應(yīng)平臺的JVM,Java程序就可以在任何平臺上運行,不需要做任何修改。這就是“一次編寫,到處運行(Write Once, Run Anywhere, WORA)”的理念。
靜態(tài)編譯技術(shù)介紹
然而,在當前出現(xiàn)了眾多靜態(tài)編譯技術(shù)。
例如:
預(yù)先編譯(AOT,Ahead-Of-Time Compilation):這涉及到將Java字節(jié)碼提前轉(zhuǎn)換成特定平臺的機器碼,而不是依賴于運行時的即時編譯(JIT)。這樣的方法有助于縮短JVM的啟動時間,并優(yōu)化程序的執(zhí)行效率。
GraalVM的原生鏡像(Native Image):GraalVM提供了一種機制,可以將Java應(yīng)用程序轉(zhuǎn)換成所謂的“原生映像”,這種映像能夠在操作系統(tǒng)層面直接運行,無需JVM的介入。
無論是通過AOT編譯將代碼提前轉(zhuǎn)換成機器碼,還是利用Native Image技術(shù)消除對JVM的依賴,最終生成的程序都是與特定平臺綁定的。
什么是AOT編譯?
AOT(Ahead-Of-Time Compilation)編譯是指在程序運行之前,將Java字節(jié)碼編譯成特定平臺的機器碼。這種方式可以減少JVM啟動時間并提高程序運行效率。
為什么Java要走回頭路?
Java之所以采用AOT和Native Image等技術(shù),犧牲一定的平臺無關(guān)性,主要是為了適應(yīng)云原生和Serverless的環(huán)境。在這些環(huán)境中,平臺無關(guān)性的重要性有所下降,而性能、資源利用率和部署的簡便性變得更加重要。
特定平臺的機器碼:AOT編譯生成的是針對特定操作系統(tǒng)和硬件架構(gòu)的機器碼。這意味著編譯后的程序只能運行在與編譯目標相匹配的平臺上,與Java字節(jié)碼的跨平臺特性不同。
缺少JVM抽象層:Java的跨平臺特性很大程度上依賴于JVM提供的抽象層,它使得字節(jié)碼可以在任何安裝了相應(yīng)JVM的平臺上運行。而AOT編譯繞過了JVM,直接生成機器碼,因此失去了這種抽象層。
部署復(fù)雜性:當應(yīng)用程序使用AOT編譯后,可能需要為每個目標平臺編譯一個特定版本的程序。這增加了部署的復(fù)雜性和工作量,因為開發(fā)者需要管理多個版本的應(yīng)用程序。
維護成本:隨著平臺數(shù)量的增加,維護多個版本的應(yīng)用程序變得更加困難和昂貴。這與Java平臺無關(guān)性帶來的便利性形成了對比。
性能和資源權(quán)衡:AOT編譯通常用于提高程序的性能和減少啟動時間,但這是以犧牲平臺無關(guān)性為代價的。在某些場景下,這種權(quán)衡是可接受的,尤其是在性能要求高、平臺相對固定的環(huán)境中。
云原生和容器化:在云原生和容器化環(huán)境中,應(yīng)用程序通常運行在隔離的環(huán)境中(如Docker容器或Kubernetes集群),這些環(huán)境可以隱藏底層硬件和操作系統(tǒng)的差異。在這種情況下,AOT編譯帶來的平臺依賴性問題不那么明顯。