Java還是最佳選擇嗎?
去年 8 月份的 the Oracle Groundbreakers Tour 2020 LATAM 大會上,Mark Nelson 和 Peter Nagy 就做過一系列基礎(chǔ)的的測試用以比較兩者。接下來就給大家介紹下。
在程序員圈子里,普遍的看法是 Java 老、慢、無聊 ,而 Go 是快、新、酷。
為了盡可能的進行一個相對公平的測試,他們使用了一個非常簡單的微服務(wù),沒有外部依賴關(guān)系(比如數(shù)據(jù)庫),代碼路徑非常短(只是操縱字符串),使用了小型的、輕量級的框架(Helidon for Java 和 Go 工具包 for Go),試驗了不同版本的 Java 和不同的 jvm。
對決雙雄我們先來看下擂臺兩邊的選手:
身穿深色戰(zhàn)服的選手是 JAVAJava 是由被甲骨文收購的 Sun Microsystems 開發(fā)的。它的 1.0 版本是 1996 年發(fā)布的,最新的版本是 2020 年的 Java15。主要的設(shè)計目標(biāo)是 Java 虛擬機和字節(jié)碼的可移植性,以及帶有垃圾收集的內(nèi)存管理。它是全世界最流行的語言之一,在開源環(huán)境下開發(fā)。
我們先看下 JAVA 的問題,大家普遍認(rèn)為它最大的問題就是速度慢,已經(jīng)慢到讓人覺得不再是合理的,而是更具歷史意義的。不過這么多年來,Java 誕生了很多不同的垃圾收集算法用來加快它運行的速度。
Oracle 實驗室最近已經(jīng)開發(fā)了一個新的 Java 虛擬機 GraalVM,它有一個新的編譯器和一些令人興奮的新特性,比如能夠?qū)?Java 字節(jié)碼轉(zhuǎn)換成一個本機映像,可以在沒有 javavm 的情況下運行等。
而它的對手就是年輕充滿活力的 GOGO 是由谷歌的羅伯特·格里默、羅伯·派克和肯·湯姆森創(chuàng)建的。他們對 UNIX、B、C、Plan9、UNIX 窗口系統(tǒng)等做出了重大貢獻。GO 是開源的,在 2012 年發(fā)布了 1.0 版本(比 JAVA 晚了 16 年),在 2020 年發(fā)布了 1.15 版本。無論是在采用方面,還是在語言和工具生態(tài)系統(tǒng)本身方面,它都在快速增長。
GO 受 C、Python、JavaScript 和 C++等多種語言的影響。被設(shè)計成高性能網(wǎng)絡(luò)和多處理的最佳語言。
StackOverflow 有 27872 個帶“Go”的問題,而 Java 只有 1702730個。足見長江后浪推前浪。
Go 是一種靜態(tài)類型的編譯語言。它有稱為 goroutines 的輕量級進程(這些不是 OS 線程),它們之間有獨特的通信通道(類型化的,F(xiàn)IFO)。Go 是許多 CNCF 項目的首選語言,例如 Kubernetes、Istio、Prometheus 和 Grafana
賽前對比從個人感覺來說,Go 相比 JAVA 來說,優(yōu)點在于:
Go 更容易實現(xiàn)復(fù)合、純函數(shù)、不變狀態(tài)等功能模式。Go 處于生命周期的早期,因此它沒有向后兼容性的沉重負(fù)擔(dān)—Go 仍然可以輕易打破某些限制來改進。Go 編譯成一個本機靜態(tài)鏈接的二進制文件-沒有虛擬機層-二進制文件擁有運行程序所需的一切,這對于“從頭開始”的容器來說非常好。Go 體積小、啟動快、執(zhí)行快(目前是的)Go 沒有 OOP,繼承,泛型,斷言,指針?biāo)惴℅o 寫法上較少的括號Go 沒有循環(huán)依賴、沒有未使用的變量或?qū)?、沒有隱式類型轉(zhuǎn)換的強制Go 樣板代碼少得多缺點是:
Go 工具生態(tài)系統(tǒng)還不成熟,尤其是依賴關(guān)系管理——有幾個選項,沒有一個是完美的,特別是對于非開源開發(fā);仍然存在兼容性挑戰(zhàn)。構(gòu)建具有新的/更新的依賴項的代碼非常慢(比如 Maven 著名的“下載Internet”問題)導(dǎo)入將代碼綁定到存儲庫,這使得在存儲庫中移動代碼成為一場噩夢。調(diào)試、評測等仍然是一個挑戰(zhàn)用到了指針需要實現(xiàn)一些基本的算法沒有動態(tài)鏈接沒有太多旋鈕來調(diào)優(yōu)執(zhí)行或垃圾收集、概要文件執(zhí)行或優(yōu)化算法。比賽開始使用 JMeter 來運行負(fù)載測試。這些測試多次調(diào)用這些服務(wù),并收集有關(guān)響應(yīng)時間、吞吐量(每秒事務(wù)數(shù))和內(nèi)存使用情況的數(shù)據(jù)。對于 Go,收集駐留集大小;對于 Java,跟蹤本機內(nèi)存。
在測量之前,使用 1000 次服務(wù)調(diào)用對應(yīng)用程序進行預(yù)熱。
應(yīng)用程序本身的源代碼以及負(fù)載測試的定義都在這個 GitHub 存儲庫中:https://github.com/markxnelson/go-java-go
第一回合
在第一輪測試中,在一臺“小型”機器上進行了測試,是一臺 2.5GHz 雙核 Intel core i7 筆記本電腦,16GB 內(nèi)存運行 macOS。測試運行了 100 個線程,每個線程有 10000 個循環(huán),上升時間為 10 秒。Java 應(yīng)用程序運行在 JDK11 和 Helidon2.0.1上。使用 Go 1.13.3 編譯的 Go 應(yīng)用程序。
結(jié)果如下:
可以看出,第一回合是 Go 贏了!
JAVA 占的內(nèi)存太多了;預(yù)熱對 JVM 有很大的影響—我們知道 JVM 在運行時會進行優(yōu)化,所以這是有意義的
在第一回合的基礎(chǔ)上,意猶未盡的又引入 GraalVM 映像以使 Java 應(yīng)用程序的執(zhí)行環(huán)境更接近于 Go 應(yīng)用程序的環(huán)境,添加了 GraalVM 映像測試(用 GraalVM EE 20.1.1ー JDK 11 構(gòu)建的本機映像)的結(jié)果是:
通過使用 GraalVM 映像在 JVM 上運行應(yīng)用程序,我們沒有看到吞吐量或響應(yīng)時間方面的任何實質(zhì)性改進,但是內(nèi)存占用的確變小了。
下面是一些測試的響應(yīng)時間圖:
第二回合
在第二輪測試中,使用一臺更大的機器上運行測試。36 核(每個核兩個線程)、256GB 內(nèi)存、運行 oraclelinux7.8 的機器。
和第一輪類似,使用了 100 個線程,每個線程使用了 10,000 個循環(huán),10 秒的加速時間,以及相同版本的 Go,Java,Helidon 和 GraalVM。
結(jié)果如下:
這一回合是 GraalVM 映像贏了!
下面是一些測試的響應(yīng)時間圖:
在這個測試中,Java 變體的表現(xiàn)要好得多,并且在沒有使用 Java 日志記錄的情況下,它的性能大大超過了 Go。Java 似乎更能使用硬件提供的多核和執(zhí)行線程(與 Go 相比)。
這一輪的最佳表現(xiàn)來自 GraalVM native image,平均響應(yīng)時間為 0.25 毫秒,每秒事務(wù)數(shù)為 82426 個,而 Go 的最佳結(jié)果為 1.59 毫秒和 39227 個 tps,然而這是以多占用兩個數(shù)量級的內(nèi)存為代價的!
GraalVM 映像比在 jvm 上運行的同一應(yīng)用程序快大約 30–40%!
第三回合這次,比賽在 Kubernetes 集群中運行這些應(yīng)用程序,這是一個更自然的微服務(wù)運行時環(huán)境。
這次使用了一個 Kubernetes 1.16.8 集群,它有三個工作節(jié)點,每個節(jié)點有兩個內(nèi)核(每個內(nèi)核有兩個執(zhí)行線程)、14GB 的 RAM 和 oraclelinux7.8。
應(yīng)用程序訪問是通過 Traefik 入口控制器進行的,JMeter 在 Kubernetes 集群外運行,用于一些測試,而對于其他測試,使用 ClusterIP 并在集群中運行 JMeter。
與前面的測試一樣,使用了 100 個線程,每個線程使用了 10,000 個循環(huán),以及 10 秒的加速時間。
下面是各種不同容器的大小:
Go 11.6MB 11.6 MBJava/Helidon 1.41GB 1.41 GBJava/Helidon JLinked 150MB 150mbNative image 25.2MB 25.2 MB結(jié)果如下:
下面是一些測試的響應(yīng)時間圖:
在這一輪中,我們觀察到 Go 有時更快,GraalVM 映像有時更快,但這兩者之間的差別很小(通常小于 5%)。
Java 似乎比 Go 更善于使用所有可用的內(nèi)核/線程—在 Java 測試中看到了更好的 CPU 利用率。Java 性能在擁有更多內(nèi)核和內(nèi)存的機器上更好,Go 性能在較小/功能較弱的機器上更好。在一臺“生產(chǎn)規(guī)?!钡臋C器上,Java 很容易就和 Go 一樣快,或者更快。
Go語言出后,Java還是最佳選擇嗎?不,Java還是很香的