JVM-Ubuntu18.04.1下編譯OpenJDK8
近期開始學習 JVM ,看的是周老師的《深入理解 Java 虛擬機器》 , 打算先自己編譯個 JDK 來提升對 JVM 的興趣。本文分三部分來描述編譯 OpenJDK 的過程,分別是 編譯前準備工作 、構建編譯環境、 進行編譯 ,在這三部分內容中順帶把趟的坑一起說明下。
一 、編譯前準備工作
1.1 安裝 Linux 環 境
編譯 OpenJDK , Windows 環境要比 Linux 環境複雜,故選擇安裝 Linux 環境。可以自己在 Windows 下安裝虛擬機器,也可以直接安裝雙系統,專門空出一個磁碟來安裝 Linux ,我選擇了後者,安裝最新版的 Ubuntu 18.04.1 LTS ,搭建雙系統的過程這裡不再贅述,網路上有非常多的材料可以參考。我的電腦是 ThinkPad-E480 ,安裝完成後巨坑,無法連線 WIFI ,害得我花了一個晚上來解決這個問題,附上解決問題連結:
ofollow,noindex"> https://askubuntu.com/questions/1070593/lenovo-thinkpad-e480-no-wifi-adaptor-found-in-ubuntu-18-04
1.2 下載 OpenJDK 原始碼
原本是計劃按照周老師的書一步一步的操作,所以計劃的是編譯 OpenJDK7 , OpenJDK7 對應的 BootStrapJDK 是 OpenJDK6 ,無奈 OpenJDK6 在 Ubuntu 18.04.1 LTS 上很難再找到資源,故放棄了這個思路,改成編譯 OpenJDK8 , BootStrapJDK 是 OpenJDK7 ,實踐證明這個操作也是一路的坑,後面環節再敘述。
確定了思路後,接下來就是下載 OpenJDK8 的原始碼,有兩種方式:
第一種就是 Mercurial ,優點就是操作起來很簡單,不需要再解壓檔案包,缺點就是需要耗費的時間長一些,實際上本人最終就是使用的這種方式,預計耗時半小時左右。 Mercurial 也是一種版本管理工具,大家可以想象下 SVN 、 Git 之類的工具。下載程式碼的命令如下:
hg clone http://hg.openjdk.java.net/jdk8u/jdk8u-dev cd jdk8u-dev sh get_source.sh
第二種就是手動方式,說白了就是自己去下載原始碼包,然後解壓,優點就是耗時短,但相對來說如果不會查詢資源,就只能下載到老版本的原始碼,比如我就只找到這個連結 http://jdk.java.net/java-se-ri/8 下的原始碼,這個版本是 2015 年的版本,距離現在已經過去了三年,這樣的程式碼其實在後面的編譯過程中如果遇到一些問題就無法判斷是 Linux 的問題還是 OpenJDK8 的程式碼問題。
到目前為止,已經有了 Linux 作業系統,需要編譯的 OpenJDK 原始碼也已經有了,下一個環節便是思考如何構建編譯環境。
二、構建編譯環境
學習了這麼多年,大家應該都具備了一定的學習方法。做 IT 的一個很重要的學習方法就是在拿到資料後,最好先翻閱下這個資料的 DEMO 或者是 README 之類的。同樣, OpenJDK 原始碼目錄下也有這樣一個檔案,叫做 README-builds.html 。
這個檔案基本上貫穿了咱們本文的操作流程,首先來看下 Introduction :
-
The build is now a "
configure && make
" style build -
Any GNU make 3.81 or newer should work
-
The build should scale, i.e. more processors should cause the build to be done in less wall-clock time
-
Nested or recursive make invocations have been significantly reduced, as has the total fork/exec or spawning of sub processes during the build
-
Windows MKS usage is no longer supported
-
Windows Visual Studio
vsvars*.bat
andvcvars*.bat
files are run automatically -
Ant is no longer used when building the OpenJDK
-
Use of ALT_* environment variables for configuring the build is no longer supported
和 OpenJDK7 的構建相比,已經不再需要 Ant ,另外 ALT_* 的環境變數也不再支援, OpenJDK7 的編譯過程可檢視周老師的書,也可以網上查閱其他資料。
檔案的第二部分內容是下載原始碼,目前程式碼下載環節已在本文 1.2 中體現,這裡不再贅述。
第三部分就是 Building ,這裡聲明瞭各個作業系統環境中的軟體硬體要求,明確要求了 OpenJDK8 的 boot JDK 是 JDK 7 。
2.1 安裝 boot JDK
在檔案中的 Specific Developer Build Environments 部分實際也約定了如何安裝 boot JDK ,命令如下(在 Ubuntu 18.04.1 LTS 中 aptitude
應該改成 apt-get
):
sudo aptitude build-dep openjdk-7 sudo aptitude install openjdk-7-jdk
實際執行下來,如上命令也是不成功的,提示沒有可安裝候選,這個也就是本文 1.2 提到的其中一個坑,該如何解決呢?請看 https://askubuntu.com/questions/761127/how-do-i-install-openjdk-7-on-ubuntu-16-04-or-higher , 裡面的 ppa
方式也已經過期了,只能按
MDMower
描述的方案來操作,我這邊選擇了
Manual Installation ,最終成功安裝
boot JDK
,結果如下:
lingjiango@lingjiango-ThinkPad-E480:~$ java -version java version "1.7.0_161" OpenJDK Runtime Environment (IcedTea 2.6.12) (7u161-2.6.12-1) OpenJDK 64-Bit Server VM (build 24.161-b01, mixed mode)
2.2 依賴檢查
實際上如果是按照 README-builds.html 的流程,在安裝 boot JDK
之前是先進行依賴檢查的,即使沒有先
安裝 boot JDK
,直接通過
bash ./configure
來檢查的話,這步最先提示的也是安裝
boot JDK
,提示如下:
configure: Could not find a valid Boot JDK. You might be able to fix this by running 'sudo apt-get install openjdk-7-jdk'. configure: This might be fixed by explicitely setting –with-boot-jdk
在完成本文
2.1
後,接下來就是遞迴執行
bash ./configure
來檢查編譯環境的依賴項是否全部安裝完成。直到看到這個結果:
==================================================== A new configuration has been successfully created in /home/lingjiango/jdk8u-dev/build/linux-x86_64-normal-server-release using default settings. Configuration summary: * Debug level:release * JDK variant:normal * JVM variants:server * OpenJDK target: OS: linux, CPU architecture: x86, address length: 64 Tools summary: * Boot JDK:java version "1.7.0_161" OpenJDK Runtime Environment (IcedTea 2.6.12) (7u161-2.6.12-1) OpenJDK 64-Bit Server VM (build 24.161-b01, mixed mode)(at /usr/lib/jvm/java-7-openjdk-amd64) * Toolchain:gcc (GNU Compiler Collection) * C Compiler:Version 7.3.0 (at /usr/bin/gcc) * C++ Compiler:Version 7.3.0 (at /usr/bin/g++) Build performance summary: * Cores to use:7 * Memory limit:7872 MB
這裡再補充說明下,在遞迴執行依賴檢查的過程中可能會提示這個 lib
x
11-dev
,
Ubuntu 18.04.1 LTS 是這麼提示安裝專案的:
sudo apt-get install libX11-dev libxext-dev libxrender-dev libxtst-dev libxt-dev
lib
X
11-dev
的
X
是大寫的,應該會提示找不到這個依賴項,這個時候要把大寫
X
改成小寫的
x
,為
lib
x
11-dev
,就可以找到依賴項了。
到這一步,
OpenJDK8
的編譯環境就已經準備好了,下一步就是編譯
OpenJDK8
。
三、進行編譯
編譯的程式碼很簡單,直接 make all 即可,當然也可以按照 README-builds.html 中對 make 執行帶引數編譯,說明如下:
Make Target |
Description |
---|---|
empty |
build everything but no images |
|
build everything including images |
|
build all configurations |
|
create complete j2sdk and j2re images |
|
install the generated images locally, typically in |
|
remove all files generated by make, but not those generated by |
|
remove all files generated by both and |
|
give some help on using |
在編譯前還有幾個注意事項,這些注意事項在檔案 README-builds.html 中也是有體現的:
設定語言選項,可先執行 echo $LANG ,看下輸出,如果不是 C ,則執行 export LANG=C ;
設定 PATH ,可先執行 echo $PATH ,看下輸出,如果沒有 boot JDK ,則執行 export PATH= "/usr/lib/jvm/java-7-openjdk-amd64/bin:${PATH}"
;
檢查 JAVA_HOME ,可先執行 echo $JAVA_HOME ,看下輸出,如果有值則需要 unset JAVA_HOME
;
這三步檢查執行通過後,就可以執行
make
命令了。
一切順利的話,就可以看到這樣的編譯結果:
## Finished docs (build time 00:01:46) ----- Build times ------- Start 2018-09-23 16:59:30 End2018-09-23 17:08:39 00:00:19 corba 00:00:13 demos 00:01:46 docs 00:03:26 hotspot 00:00:18 images 00:00:13 jaxp 00:00:17 jaxws 00:02:01 jdk 00:00:25 langtools 00:00:11 nashorn 00:09:09 TOTAL ------------------------- Finished building OpenJDK for target 'all'
看到這樣的結果,表示編譯成功,可以到多個目錄下的
bin
目錄執行
./java -version
來驗證。
lingjiango@lingjiango-ThinkPad-E480:~/jdk8u-dev/build/linux-x86_64-normal-server-release/jdk/bin$ ./java -version openjdk version "1.8.0-internal" OpenJDK Runtime Environment (build 1.8.0-internal-lingjiango_2018_09_23_16_59-b00) OpenJDK 64-Bit Server VM (build 25.71-b00, mixed mode) lingjiango@lingjiango-ThinkPad-E480:~/jdk8u-dev/build/linux-x86_64-normal-server-release/images/j2sdk-image/bin$ ./java -version openjdk version "1.8.0-internal" OpenJDK Runtime Environment (build 1.8.0-internal-lingjiango_2018_09_23_16_59-b00) OpenJDK 64-Bit Server VM (build 25.71-b00, mixed mode)
事實上,我在編譯的過程中就不順利,主要遇到了兩個問題:
1、編譯核心版本問題
在本文
1.2
中已經提到了兩種獲取原始碼的方式,其實一開始我採用的是方法二,下載的是
2015
年的
openjdk-8u40
,這個原始碼包中的
/hotspot/make/linux/Makefile
檔案中宣告的
SUPPORTED_OS_VERSION
不支援
4.X
的核心,所以編譯報如下截圖的錯誤:
因為 Ubuntu 18.04.1 LTS 的核心是 4.15.0-34-generic ,故如果要繼續編譯下去,需要將 Makefile 的 SUPPORTED_OS_VERSION 那行後面新增 4% 。
2 、 -Werror=deprecated-declarations 問題
在我把問題 1 解決後,繼續編譯,後面又碰到了很多神奇的問題,而且很難查詢到相關解決問題的資料。所以我只能從邏輯上推理下, OpenJDK8 一直在更新發展, Ubuntu 也一直在更新發展,兩者同步更新,應該取最新的檔案編譯起來問題才會少一些,而且猜測也有更多的資料可查,但是現在用的是 2015
年的
openjdk-8u40
,而
Ubuntu 又是最新的,所以有問題估計也沒有人去修復(其實我們的很多應用系統一樣也是這個道理,年久失修,沒什麼人用的功能有問題也不一定去修復)。這個時候我果斷切換到最新的 OpenJDK8 ,通過 Mercurial 下載最新的程式碼,然後在 Ubuntu 18.04.1 LTS 編譯。編譯的過程就碰到一個問題,報錯如下:
os_linux.inline.hpp:127:18: error: 'int readdir_r(DIR*, dirent*, dirent**)' is deprecated [-Werror=deprecated-declarations]
查閱網上資料說是這是因為 glibc >= 2.24 的情況下,方法 readdir_r 被 deprecated ,不支援了,通過 getconf GNU_LIBC_VERSION 檢查發現 Ubuntu 18.04.1 LTS 版本為 glibc 2.27 ,而且也有很多人在 OpenJDK 上報了 BUG ,連結 https://bugs.openjdk.java.net/browse/JDK-8179887 , 6/7/8/9 都不打算修復此問題,會在 11 修復這個 BUG ,所以當前只能通過其他的方式來解決,解決方案如下:
在 ./hotspot/make/linux/makefiles/gcc.make 檔案中找到 WARNINGS_ARE_ERRORS = -Werro, 註釋該段或改成 WARNINGS_ARE_ERRORS = -Wno-all 。再編譯就會忽略掉警告,直到編譯完成。
參考資料:
《深入理解 Java 虛擬機器》
README-builds.html