故障排查)
簡(jiǎn)介JDK 1.8.0_231 的 64 位免安裝壓縮包面向需要在本地或服務(wù)器上快速搭建 Java 8 開(kāi)發(fā)環(huán)境的開(kāi)發(fā)者與教學(xué)場(chǎng)景。它省去安裝向?qū)Ы鈮汉笈渲?JAVA_HOME、PATH 等變量即可編譯并運(yùn)行程序適合多環(huán)境切換、離線部署與舊項(xiàng)目維護(hù)。壓縮包共 1473 個(gè)文件約 172.9MB以 705 個(gè) jar核心類(lèi)庫(kù)與工具組件、118 個(gè) dll 與 74 個(gè) exe本地庫(kù)與可執(zhí)行程序?yàn)橹髁砗?228 個(gè) xml、50 個(gè) properties 配置文件和若干 HTML、圖片等文檔資源目錄結(jié)構(gòu)基本保留了官方發(fā)行版的完整形態(tài)。該版本包含 Java 8 的 Lambda 表達(dá)式、Stream API 與 java.time 日期時(shí)間 API 等特性并附帶 Java Mission Control 等診斷工具可用于排查內(nèi)存與性能問(wèn)題。目前已有 2073 人學(xué)習(xí)下載適合需要穩(wěn)定 64 位 JDK 環(huán)境、希望獲得完整工具鏈的初、中級(jí) Java 開(kāi)發(fā)者。1. 裝 JDK 1.8 前先把 8u231 這個(gè)版本號(hào)認(rèn)清楚構(gòu)建機(jī)上前腳剛把 JDK 升到 11后腳項(xiàng)目一編譯就拋出Unsupported class file major version 55想退回去裝 jdk1.8 安裝包又卡在 JAVA_HOME 上反復(fù)折騰。這種來(lái)回折騰在還跑著老項(xiàng)目的團(tuán)隊(duì)里幾乎每周都會(huì)發(fā)生。大量老中間件、老構(gòu)建腳本、老 IDE 工程仍然鎖定在 JDK 1.8 上而 2019 年那批更新里的 jdk1.8.0_231 64 位版本是這幾年被復(fù)用最多的一份——zip 是免安裝的解壓后配好環(huán)境變量就能用。也正因?yàn)樗粚?xiě)注冊(cè)表、不自動(dòng)配環(huán)境jdk1.8 64位下載之后最常出現(xiàn)的狀況是java 能跑、javac 找不到或者兩個(gè)命令的版本對(duì)不上。這篇記錄的是我完整走一遍的過(guò)程從校驗(yàn)壓縮包到跑通冒煙測(cè)試新手照著做能走通熟手重點(diǎn)看版本取舍和后面那五條排查。2. 解壓 jdk1.8.0_231.zip包結(jié)構(gòu)、校驗(yàn)和目錄落位拿到壓縮包之后先不要急著雙擊。綠色版 JDK 的好處是干凈壞處是它把“配置”這部分責(zé)任全部交回給你所以第一步必須弄清楚你手里這個(gè)包到底是什么結(jié)構(gòu)、該放到哪里。2.1 zip 里到底有什么跟 exe 安裝包差在哪把jdk1.8.0_231.zip解開(kāi)你會(huì)看到一個(gè)頂層目錄jdk1.8.0_231/里面是bin、jre、lib、include、db、man這幾樣。bin下面同時(shí)有java.exe和javac.exe這一點(diǎn)很關(guān)鍵——后面排查“java 有 javac 沒(méi)有”的時(shí)候靠的就是確認(rèn) JAVA_HOME 有沒(méi)有指到這一層。jre目錄在 JDK 8 里是包內(nèi)自帶的一份和 JDK 本體放在一起。這是 1.8 的結(jié)構(gòu)特點(diǎn)從 9 之后模塊化改造這種結(jié)構(gòu)就不再有。所以老教程寫(xiě)%JAVA_HOME%\jre\bin是合理的但在 1.8 里其實(shí)沒(méi)有必要往 Path 里加bin目錄已經(jīng)覆蓋了運(yùn)行和編譯所需的可執(zhí)行文件。和 exe 安裝包最大的差別在注冊(cè)表和轉(zhuǎn)發(fā)器。exe 裝完之后會(huì)在C:\Program Files\Common Files\Oracle\Java\javapath放一組java.exe、javaw.exe、javaws.exe并把該目錄寫(xiě)進(jìn)系統(tǒng) Path 最前面同時(shí)也會(huì)往C:\Windows\System32塞幾個(gè)。它做這些是為了讓用戶在任意目錄都能直接用但副作用是你后來(lái)把 JAVA_HOME 指向 APath 里排在前面的 javapath 還指著 B于是java -version正常、javac -version報(bào)錯(cuò)或者反過(guò)來(lái)。zip 綠色版沒(méi)有這個(gè)問(wèn)題代價(jià)就是環(huán)境變量得自己一項(xiàng)一項(xiàng)配。我一般會(huì)先確認(rèn)壓縮包有沒(méi)有被截?cái)?。正?Windows 64 位版的 jdk1.8.0_231 壓縮包在一百多兆到兩百兆之間如果看到只有幾十 KB 或者幾 MB基本可以判斷是下載中斷、下成了 HTML 錯(cuò)誤頁(yè)或者被某些下載工具“優(yōu)化”過(guò)。這類(lèi)包解壓時(shí)可能不報(bào)錯(cuò)但bin里會(huì)缺文件后面編譯時(shí)才會(huì)出現(xiàn)各種莫名奇異的錯(cuò)誤最難排查。2.2 校驗(yàn)與解壓路徑里別留空格和中文下載完成后先做一次哈希校驗(yàn)。Windows 上自帶 certutil:: Windows 下校驗(yàn) zip 完整性把輸出和你手上的 sha256 對(duì)一下 certutil -hashfile D:\download\jdk1.8.0_231.zip SHA256Linux 下我更習(xí)慣用 sha256sum# 校驗(yàn)通過(guò)再進(jìn)行解壓避免解到一半才發(fā)現(xiàn)包壞了 sha256sum jdk1.8.0_231.zip校驗(yàn)這一步很多人會(huì)跳過(guò)但它是唯一能在解壓之前確認(rèn)“這個(gè)包是完整的”的手段。遇到過(guò)一次壓縮包只下到 90%解壓工具不報(bào)錯(cuò)lib\tools.jar是 0 字節(jié)結(jié)果所有用到 tools.jar 的老腳本全掛排查了大半天才回到源頭上。解壓工具的選擇也有講究。Windows 資源管理器對(duì) zip 支持沒(méi)問(wèn)題但它不處理超長(zhǎng)路徑如果解壓目標(biāo)路徑本身就長(zhǎng)會(huì)出現(xiàn)“解壓成功但文件少了”。我一般用 7-Zip 右鍵“解壓到 jdk1.8.0_231\”解壓后統(tǒng)計(jì)一下文件數(shù)再和壓縮包內(nèi)置的目錄結(jié)構(gòu)對(duì)一眼。Linux 上# 沒(méi)裝 unzip 先裝apt install unzip / yum install unzip # -d 指定目標(biāo)目錄-q 靜默-o 覆蓋已存在文件 unzip -q -o jdk1.8.0_231.zip -d /usr/local/路徑規(guī)則只有一條不要有空格、中文、、(、)。D:\Program Files\jdk1.8.0_231這種是典型反例很多老構(gòu)建腳本、老批處理在拼接路徑時(shí)不做引號(hào)轉(zhuǎn)義空格會(huì)直接把參數(shù)截?cái)鄨?bào)出來(lái)的錯(cuò)誤跟你實(shí)際做的事毫無(wú)關(guān)系。同理D:\工具\(yùn)jdk也不行某些控制臺(tái)程序讀中文路徑會(huì)亂碼。我統(tǒng)一用D:\env和/usr/local路徑短、純 ASCII、權(quán)限模型清晰。2.3 目錄落位我一般用 /usr/local 和 D:\envWindows 上我把它放在D:\env\jdk1.8.0_231和 Maven、Node、Python 這些放一起方便統(tǒng)一備份和遷移。放 C 盤(pán)的問(wèn)題不在權(quán)限而在于重裝系統(tǒng)或者做鏡像時(shí)容易丟而且 C 盤(pán)空間經(jīng)常告急JDK 幾萬(wàn)個(gè)文件堆在那里不合適。Linux 上用的是/usr/local/jdk1.8.0_231然后建一個(gè)軟鏈# 建軟鏈好處是升級(jí)時(shí)只改指向不用動(dòng)所有引用它的腳本 ln -sfn /usr/local/jdk1.8.0_231 /usr/local/java ls -l /usr/local/java-s建軟鏈-f強(qiáng)制覆蓋已有文件-n是關(guān)鍵——如果/usr/local/java已經(jīng)是個(gè)指向目錄的軟鏈不加-n會(huì)在那個(gè)目錄里面再建一個(gè)java軟鏈變成/usr/local/java/jdk1.8.0_231/...這種嵌套非常隱蔽。我第一次踩到的時(shí)候看了半天ls -l才反應(yīng)過(guò)來(lái)。權(quán)限方面JDK 目錄應(yīng)該屬于 rootbin下的可執(zhí)行文件保留執(zhí)行位chown -R root:root /usr/local/jdk1.8.0_231 chmod -R 755 /usr/local/jdk1.8.0_231/bin不要把 JDK 目錄設(shè)成 777。有人圖省事這么干后來(lái)發(fā)現(xiàn)bin下的文件權(quán)限被改過(guò)之后java啟動(dòng)直接報(bào) permission denied還查不到原因。3. Windows 配環(huán)境變量JAVA_HOME、Path 和那條 where 命令綠色版在 Windows 上的全部工作量就是三處環(huán)境變量但順序和寫(xiě)法錯(cuò)一個(gè)字符就會(huì)出現(xiàn)“明明配了卻用不上”的現(xiàn)象。3.1 JAVA_HOME 放在系統(tǒng)變量里值不要帶 \bin右鍵“此電腦” → 屬性 → 高級(jí)系統(tǒng)設(shè)置 → 環(huán)境變量。新建變量建在系統(tǒng)變量里不是用戶變量。原因是很多場(chǎng)景——Windows 服務(wù)、計(jì)劃任務(wù)、以 SYSTEM 賬號(hào)運(yùn)行的 CI agent——讀的是系統(tǒng)變量用戶變量在它們眼里根本不存在。用戶變量里配一份、系統(tǒng)變量里配另一份并且不一致是最容易讓人崩潰的組合。變量名JAVA_HOME值D:\env\jdk1.8.0_231。三個(gè)細(xì)節(jié)不要帶\bin。帶了的后果是 Path 里%JAVA_HOME%\bin展開(kāi)成D:\env\jdk1.8.0_231\bin\bin然后java找不到報(bào)“不是內(nèi)部或外部命令”。不要帶末尾分號(hào)。分號(hào)在 Windows 上是路徑分隔符寫(xiě)在值末尾會(huì)讓展開(kāi)后多出一個(gè)空路徑段某些程序解析時(shí)行為不確定。不要在值里加引號(hào)。環(huán)境變量的值原樣保存加了引號(hào)引號(hào)會(huì)變成路徑的一部分。然后編輯系統(tǒng)變量Path新增兩條并且用上移按鈕把它們挪到最前面%JAVA_HOME%\bin %JAVA_HOME%\jre\bin第二條在 1.8 里嚴(yán)格來(lái)說(shuō)不是必需的但加了不會(huì)有副作用。真正決定成敗的是先后順序Windows 查找可執(zhí)行文件是按 Path 變量從前往后掃描取第一個(gè)命中的。如果你的機(jī)器上曾經(jīng)裝過(guò) exe 版 JDK 或者 JREPath 里通常已經(jīng)有C:\Program Files\Common Files\Oracle\Java\javapath和C:\Windows\System32這兩個(gè)目錄里都有java.exe。不把它們挪后或者刪掉java -version顯示的永遠(yuǎn)是它們你在 JAVA_HOME 里改什么都沒(méi)用。3.2 CLASSPATH 到底要不要配這是老教程遺留最多的一處。十年前的標(biāo)準(zhǔn)寫(xiě)法是.;%JAVA_HOME%\lib\dt.jar;%JAVA_HOME%\lib\tools.jar在 JDK 8 下這個(gè)配置能工作但基本是冗余的。dt.jar是 IDE 用來(lái)做 BeanInfo 顯示的tools.jar主要供編譯器和工具使用應(yīng)用運(yùn)行時(shí)并不依賴(lài)它們。而最前面的那個(gè)點(diǎn)表示當(dāng)前目錄在現(xiàn)代 JDK 上也已經(jīng)是-cp缺省時(shí)的默認(rèn)行為。我的做法是如果項(xiàng)目或構(gòu)建腳本明確要求讀 CLASSPATH就配否則不配讓它空著??罩臅r(shí)候應(yīng)用的類(lèi)路徑完全由命令行-cp或者 manifest 決定出了問(wèn)題看啟動(dòng)腳本就行不用再考慮環(huán)境變量這一層干擾。一定要配的話有兩個(gè)坑。第一開(kāi)頭那個(gè)點(diǎn)別丟丟了之后連當(dāng)前目錄下的HelloWorld.class都找不到報(bào)NoClassDefFoundError第二一定用分號(hào)不能用逗號(hào)或者中文分號(hào)某些輸入法下很容易打錯(cuò)控制臺(tái)不會(huì)提示只會(huì)在運(yùn)行時(shí)告訴你找不到類(lèi)。3.3 用 where 確認(rèn) java 和 javac 指向同一個(gè)目錄配完之后必須關(guān)掉所有已經(jīng)打開(kāi)的 cmd 窗口再重新開(kāi)一個(gè)。環(huán)境變量是在進(jìn)程啟動(dòng)時(shí)讀取的已經(jīng)運(yùn)行的窗口不會(huì)熱更新。這一條是“我明明配了啊怎么沒(méi)用”的頭號(hào)原因我自己也在這上面浪費(fèi)過(guò)時(shí)間。新窗口里跑:: 檢查三個(gè)最常見(jiàn)的落點(diǎn)輸出按 Path 順序排列第一條就是實(shí)際生效的 where java where javac :: java -version 輸出到 stderr重定向一下才能和后面的內(nèi)容一起看 java -version 21 javac -version :: 看 JAVA_HOME 是不是真的被改成了預(yù)期值 echo %JAVA_HOME%判斷依據(jù)很直接where java和where javac的第一行都應(yīng)該落在D:\env\jdk1.8.0_231\bin下面而且必須是同一個(gè)目錄。如果where java第一條是 javapath 而where javac是 JDK 目錄那就說(shuō)明 Path 順序沒(méi)調(diào)對(duì)回到上一步把%JAVA_HOME%\bin上移。java -version的輸出形如java version 1.8.0_231javac -version輸出javac 1.8.0_231。兩個(gè)版本號(hào)必須完全一致。不一致就說(shuō)明有兩個(gè) JDK 在同時(shí)生效通常是因?yàn)?JAVA_HOME 指向 A、Path 里排在最前面的卻是 B 的 bin 目錄。還有個(gè)更徹底的辦法直接從 JVM 自己嘴里問(wèn)出它在哪:: 打印 JVM 視角下的所有關(guān)鍵屬性java.home 就是它自己認(rèn)為的家目錄 java -XshowSettings:properties -version 21 | findstr /C:java.home /C:java.versionfindstr是 Windows 版的 grep/C:表示整串匹配。這條命令看到的是運(yùn)行時(shí)真實(shí)值比看環(huán)境變量可靠——有些啟動(dòng)器會(huì)自己改寫(xiě)JAVA_HOME再拉起 JVM只有這一招能識(shí)破。在 Linux 上對(duì)應(yīng)的是grep后面還會(huì)再用一次。4. Linux 裝 jdk1.8.0_231profile.d、軟鏈和 alternatives 的取舍Linux 這邊的復(fù)雜度不在于配什么而在于配在哪一層。同一臺(tái)機(jī)器上有登錄 shell、非登錄 shell、cron、systemd 四種運(yùn)行環(huán)境它們讀的初始化文件不一樣配錯(cuò)層就會(huì)出現(xiàn)“我登錄能跑服務(wù)跑不了”。4.1 解壓與軟鏈別把版本號(hào)寫(xiě)死在業(yè)務(wù)腳本里解壓和軟鏈在第 2 章已經(jīng)寫(xiě)過(guò)這里補(bǔ)一個(gè)權(quán)限細(xì)節(jié)如果 JDK 放在 NFS 或者某些做了 noexec 掛載的分區(qū)上java會(huì)報(bào)Permission denied文件權(quán)限看起來(lái)卻是對(duì)的。遇到這種先看掛載參數(shù)# 確認(rèn) /usr/local 所在分區(qū)沒(méi)有 noexec 之類(lèi)影響執(zhí)行的掛載選項(xiàng) mount | grep -E /usr| /usr/local軟鏈的意義在于解耦。所有腳本、服務(wù)的JAVA_HOME統(tǒng)一寫(xiě)/usr/local/java將來(lái)從 8u231 換到別的 1.8 更新版本或者臨時(shí)切到 11 做兼容性驗(yàn)證只需要重新指向軟鏈不用去翻十幾份啟動(dòng)腳本改路徑。4.2 /etc/profile.d/jdk.sh 比 ~/.bashrc 靠譜在哪推薦寫(xiě)法# /etc/profile.d/jdk.sh —— 全局生效登錄 shell 通過(guò) /etc/profile 自動(dòng) source export JAVA_HOME/usr/local/java export JRE_HOME$JAVA_HOME/jre export CLASSPATH.:$JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar export PATH$JAVA_HOME/bin:$PATH文件建好后chmod 644 /etc/profile.d/jdk.sh屬主 root。放到profile.d而不是直接改/etc/profile好處是升級(jí)或者卸載時(shí)刪一個(gè)文件就行不會(huì)污染主配置也更方便被自動(dòng)化腳本管理。為什么不寫(xiě)進(jìn)~/.bashrc因?yàn)?bashrc 只對(duì)交互式 shell 生效。cron 任務(wù)、ssh host java -version這種單條命令、CI agent 拉起的構(gòu)建進(jìn)程大多是非交互、非登錄的它們不讀 bashrcJAVA_HOME是空的。只寫(xiě) bashrc 的典型癥狀就是人登錄上去一切正常服務(wù)跑起來(lái)報(bào)JAVA_HOME not found或者直接用到了系統(tǒng)自帶的 OpenJDK。/etc/profile.d也有邊界——它由/etc/profile調(diào)用而/etc/profile只在登錄 shell 里執(zhí)行。所以 systemd 托管的服務(wù)仍然讀不到。這時(shí)候有兩個(gè)辦法# 方式一在 unit 文件里顯式聲明最可靠 [Service] EnvironmentJAVA_HOME/usr/local/java EnvironmentPATH/usr/local/java/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin# 方式二讓啟動(dòng)腳本自己 source 一遍適合不能在 unit 里改的場(chǎng)景 #!/bin/bash source /etc/profile.d/jdk.sh exec java -jar /opt/app/app.jar我傾向方式一。環(huán)境應(yīng)該跟著服務(wù)的定義走而不是依賴(lài) shell 的隱式初始化這樣systemctl show service能直接看到它拿到了什么環(huán)境排查時(shí)不用猜。4.3 alternatives 和 PATH 只能有一個(gè)說(shuō)了算機(jī)器上如果已經(jīng)存在系統(tǒng)自帶的 OpenJDK/usr/bin/java通常是個(gè)軟鏈或者 alternatives 管理的轉(zhuǎn)發(fā)器。想讓新裝的 JDK 接管這個(gè)入口# 注冊(cè)到 alternatives最后的數(shù)字是優(yōu)先級(jí)數(shù)字越大越優(yōu)先 alternatives --install /usr/bin/java java /usr/local/jdk1.8.0_231/bin/java 2000 \ --slave /usr/bin/javac javac /usr/local/jdk1.8.0_231/bin/javac \ --slave /usr/bin/jar jar /usr/local/jdk1.8.0_231/bin/jar # 交互式選擇當(dāng)前生效的版本 alternatives --config javaDebian/Ubuntu 上命令是update-alternatives參數(shù)位置略有差異但概念一致。RHEL/CentOS 用alternatives。這里有一個(gè)必須理解的沖突alternatives 改的是/usr/bin/java這個(gè)入口而/etc/profile.d/jdk.sh改的是PATH讓/usr/local/java/bin排在/usr/bin前面。兩者同時(shí)用PATH 優(yōu)先級(jí)高的那條路贏alternatives 配了也白配。選一個(gè)當(dāng)主機(jī)制我一般用 PATH 軟鏈管主要場(chǎng)景alternatives 只在需要給系統(tǒng)里其他不讀 profile 的程序提供統(tǒng)一入口時(shí)補(bǔ)一下并且確保改完的readlink -f $(which java)指向的是我預(yù)期的路徑。4.4 分別驗(yàn)證登錄 shell 和非登錄 shell配完之后分兩次驗(yàn)證這一步不能省# 登錄 shell會(huì)讀 /etc/profile進(jìn)而 source profile.d bash -lc echo $JAVA_HOME; which java; java -version # 非登錄 shell不讀 /etc/profile用來(lái)模擬 cron 和 CI agent 的視角 bash -c echo $JAVA_HOME; which java; java -version 21如果第一條正確、第二條JAVA_HOME為空但java -version也對(duì)說(shuō)明系統(tǒng)里本來(lái)就有 OpenJDK 在/usr/bin下兜底配置只是“看起來(lái)沒(méi)報(bào)錯(cuò)”。這種情況在部署服務(wù)時(shí)最容易出問(wèn)題——它用的其實(shí)是版本完全不同的另一個(gè) JDK。要消除就得把 alternatives 也指到 1.8或者在業(yè)務(wù)腳本里顯式 source。5. 裝完跑不起來(lái)五條踩坑記錄按現(xiàn)象倒著查下面這五條是我在不同機(jī)器上真實(shí)遇到過(guò)多次的。排查順序建議按“先看它到底是哪個(gè) JDK再看它缺什么”來(lái)走不要一上來(lái)就重裝。5.1 環(huán)境變量類(lèi)故障現(xiàn)象一javac 不是內(nèi)部或外部命令但java -version正常。原因通常是JAVA_HOME指到了jre子目錄或者 Path 里只加了%JAVA_HOME%\jre\bin。JDK 8 包里jre\bin有java.exe沒(méi)有javac.exe所以 java 能跑、編譯不能。 解決echo %JAVA_HOME%確認(rèn)指向的是D:\env\jdk1.8.0_231這一層不是帶\jre的那層然后用where javac確認(rèn) Path 里確實(shí)能找到。現(xiàn)象二java -version和javac -version的版本號(hào)對(duì)不上或者編譯報(bào)Unsupported class file major version 55。原因是機(jī)器上存在兩套以上 JDKPath 順序或者啟動(dòng)腳本里寫(xiě)死了另一個(gè)。IDE、Tomcat 的setenv.sh、Maven 的JAVA_HOME都是常見(jiàn)藏身點(diǎn)。 解決先用java -XshowSettings:properties -version 21 | findstr java.home拿到 JVM 自己認(rèn)為的家目錄再回頭找是誰(shuí)在把它拉起來(lái)。IDE 里要看項(xiàng)目 SDK 和運(yùn)行配置兩處只改一處不起作用?,F(xiàn)象三Linux 上登錄能跑systemctl起的服務(wù)報(bào)JAVA_HOME not found。原因就是第 4 章說(shuō)的systemd 不走 shell 初始化流程/etc/profile.d對(duì)它無(wú)效。 解決在 unit 文件里寫(xiě)Environment或者讓啟動(dòng)腳本顯式source。不要圖省事去改/etc/environment那個(gè)文件對(duì) systemd 的傳遞也有限不是所有發(fā)行版行為都一致。5.2 運(yùn)行類(lèi)故障現(xiàn)象四啟動(dòng)直接報(bào)Error occurred during initialization of VM或Could not reserve enough space for object heap。第一種可能是拿到了 32 位版本——java -version輸出里會(huì)寫(xiě)Client VM或者不帶64-Bit正常 64 位會(huì)顯示Java HotSpot(TM) 64-Bit Server VM。第二種可能是-Xmx設(shè)得超過(guò)可用內(nèi)存或容器限額。 解決先確認(rèn)是 64 位再把-Xmx降下來(lái)。容器里要額外注意JDK 8 在 8u191 之前不識(shí)別 cgroup 內(nèi)存限制會(huì)按宿主機(jī)物理內(nèi)存來(lái)算默認(rèn)堆容器被限制在 2G 卻按 32G 機(jī)器去申請(qǐng)必然失敗。8u231 已經(jīng)支持UseContainerSupport但默認(rèn)值仍然偏保守部署時(shí)顯式寫(xiě)-Xmx更穩(wěn)妥。現(xiàn)象五日志里全是中文亂碼或者讀文件報(bào)編碼異常。原因是 Java 8 的file.encoding跟隨操作系統(tǒng) locale。Windows 中文系統(tǒng)默認(rèn) GBKLinux 上多為 UTF-8同一份代碼在兩邊表現(xiàn)完全不同。 解決啟動(dòng)參數(shù)里統(tǒng)一加-Dfile.encodingUTF-8。注意這個(gè)參數(shù)對(duì)已經(jīng)初始化的流不生效必須在 JVM 啟動(dòng)時(shí)傳不能靠代碼里System.setProperty補(bǔ)救。另外javac編譯時(shí)也要加-encoding UTF-8否則源碼里的中文注釋可能導(dǎo)致編譯告警甚至報(bào)錯(cuò)。還有一個(gè)容易被誤判的PKIX path building failed。它看起來(lái)像證書(shū)問(wèn)題但很多情況下是系統(tǒng)時(shí)間不對(duì)——證書(shū)校驗(yàn)依賴(lài)準(zhǔn)確的時(shí)間窗口機(jī)器時(shí)間偏了幾小時(shí)或者幾年握手就會(huì)失敗。遇到先date看一眼再去查證書(shū)鏈。6. 裝完怎么驗(yàn)證跑通三步冒煙每次部署前都過(guò)一遍版本號(hào)對(duì)上了不代表能用。我最后一道工序是跑一個(gè)最小冒煙覆蓋三件事版本自洽、運(yùn)行時(shí)路徑正確、堆參數(shù)真的生效。第一步兩條命令的版本號(hào)必須一致java -version 21 | head -n 1 javac -version第二步編譯并運(yùn)行一個(gè)檢查類(lèi)讓 JVM 自己把關(guān)鍵屬性打印出來(lái)// EnvCheck.java —— 打印運(yùn)行時(shí)真正生效的 JDK 路徑和關(guān)鍵屬性 public class EnvCheck { public static void main(String[] args) { System.out.println(java.home System.getProperty(java.home)); System.out.println(java.version System.getProperty(java.version)); System.out.println(file.encoding System.getProperty(file.encoding)); System.out.println(java.class.path System.getProperty(java.class.path)); // 換算成 MB看 -Xmx 有沒(méi)有被真正接受 System.out.println(maxMemory(MB) Runtime.getRuntime().maxMemory() / 1024 / 1024); } }# 編譯時(shí)指定編碼避免源碼里的中文影響結(jié)果 javac -encoding UTF-8 EnvCheck.java # 顯式給一個(gè)堆上限驗(yàn)證參數(shù)確實(shí)被 JVM 接收 java -Xmx512m EnvCheckjava.home應(yīng)該指向你解壓的那個(gè)目錄Linux 上經(jīng)過(guò)軟鏈后會(huì)顯示/usr/local/java/jre這是正常的JVM 把家目錄報(bào)在 jre 層。maxMemory應(yīng)該接近但略小于 512如果它顯示的是幾個(gè) G說(shuō)明-Xmx沒(méi)生效——要么被啟動(dòng)腳本里的后置參數(shù)覆蓋要么被_JAVA_OPTIONS這類(lèi)環(huán)境變量改了那個(gè)變量會(huì)覆蓋命令行參數(shù)優(yōu)先級(jí)比你想的高。第三步打一個(gè) jar 跑一下確認(rèn) manifest 和 classpath 都通# 打包時(shí)指定入口類(lèi)模擬真實(shí)部署形態(tài) jar -cvfe EnvCheck.jar EnvCheck EnvCheck.class java -jar EnvCheck.jar三步都過(guò)基本可以判定這套 jdk1.8 安裝是通的。我整理了一張對(duì)照表出問(wèn)題時(shí)按“該看到什么”反推檢查項(xiàng)該看到的結(jié)果不符合說(shuō)明什么java -version含1.8.0_231且含64-Bit用錯(cuò)版本或拿到 32 位包javac -version1.8.0_231Path 順序錯(cuò)或 JAVA_HOME 指向 jrejava.home你解壓的那個(gè)目錄環(huán)境變量沒(méi)生效被其他 JDK 搶先f(wàn)ile.encodingUTF-8沒(méi)傳-Dfile.encoding跨平臺(tái)會(huì)亂碼maxMemory接近你設(shè)的-Xmx參數(shù)被覆蓋或被容器限額截?cái)鄰哪且院笪颐垦b一次 JDK不管多急都強(qiáng)制把這三步走完再往機(jī)器上交尤其是java.home那一行——它比任何環(huán)境變量截圖都誠(chéng)實(shí)。希望這套流程能幫到你少在版本號(hào)上繞兩圈。本文還有配套的精品資源點(diǎn)擊獲取