news 2026/10/3 3:23:36

Linux安装JDK与部署jar包实战:环境配置、后台运行与排障指南

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Linux安装JDK与部署jar包实战:环境配置、后台运行与排障指南

一台干净的Linux服务器摆在面前,任务很明确:装上JDK,把打包好的jar包跑起来,然后再让它稳定地在后台运行。这个流程我做过几十遍,也帮别人排查过各种“玄学”问题。说句实在话,安装本身不难,难的是那些零碎的坑——下载地址不知道去哪找、环境变量配了不生效、jar包起来三秒就挂、日志又没什么有价值的信息。所以这篇内容就围绕“Linux系统上安装JDK、部署jar包,以及如何指定JDK运行jar文件”这条主线展开,把版本选型、安装方式、多版本共存、后台运行、日志管理和常见故障排查一次讲透。不管你是刚接触云服务器的新手,还是在虚拟机里折腾Linux的学生,甚至是团队里负责发布的老哥们,只要目标是让一个Java程序在Linux上正常跑起来,这篇基本够用了。

1. 装JDK之前:先想清楚版本、系统架构和安装方式

很多新手上来就急着下载JDK,结果发现装到一半要么版本不对,要么系统不兼容。这一节先把前置问题理清楚,后面执行起来就会顺很多。

1.1 JDK版本怎么选:8、11、17还是21

选版本这件事直接决定你后面会不会被“缺模块”“编译报错”之类的问题折磨。我的建议是看项目实际需求,而不是追求最新:

  • 如果项目是老的Spring Boot 2.x、老版Hadoop或其他历史包袱重的技术栈,通常需要Java 8,也就是JDK 1.8,这个版本至今在不少企业里仍然是绝对主力。
  • 如果项目用的是Spring Boot 3.x,那最低要求是Java 17。很多新项目现在直接踩在17这个LTS版本上,性能和生态都比较均衡。
  • Java 11也算LTS,早期Spring Boot 2.x的某些版本支持得不错,但现在逐步边缘化。
  • Java 21是最新的LTS版本,适合全新项目,但依赖组件也要跟进,否则可能碰到兼容性问题。

建议直接选择LTS版本,别用中间版本。你搜索“jdk 17下载”或“jdk下载官网”时,注意区分Oracle JDK和OpenJDK。生产环境多数人用OpenJDK,因为免费且无调整期问题;Oracle JDK本身并不是不能用,主要是要注意授权条款。腾讯云、阿里云软件源也都有OpenJDK,直接改源后用包管理器装也行,省去不少麻烦。

1.2 确认Linux发行版和CPU架构

不同发行版安装命令不一样,CPU架构不同JDK的包也不同。登录服务器后先执行三条命令:

# 查看系统版本信息 cat /etc/os-release # 查看CPU架构,x86_64就是64位,aarch64就是ARM64 uname -m # 看看系统里是不是已经装了Java java -version

这几条命令属于最基础的Linux常用命令,但非常重要。比如你在CentOS 7上用了Ubuntu的apt,肯定装不上;你下载了x64的JDK包装到ARM服务器上,运行时会直接报“cannot execute binary file”。如果系统里已经有Java,先看版本,再决定要不要卸载重装。

1.3 明确安装方式:包管理器还是手动解压

Linux下装JDK有两条主流路线,我之前都实测过:

  1. 包管理器安装:yum install java-17-openjdk或apt install openjdk-17-jdk。优点是快、自动处理依赖和环境变量,缺点是版本受软件源控制,想装特定小版本或Oracle JDK就不方便。
  2. 手动解压tar.gz包并配置环境变量。优点是版本完全可控,想装哪个装哪个,想共存几个JDK都行,Java多版本切换很灵活,生产环境我推荐这种方式。

下面的章节我会把两条路线都详细走一遍,重点放在第二种,因为它能解决“linux指定jdk运行jar文件”这个核心诉求。

2. 安装JDK的两种主流路线

2.1 用包管理器安装:适合快速验证或临时环境

如果你只是想快速搭个环境跑一下,包管理器是最省事的。CentOS、Rocky Linux这类RedHat系用yum或dnf,Debian、Ubuntu用apt。

CentOS/RHEL系安装Java 17:

# 先搜一下可用版本 yum list available | grep openjdk # 安装Java 17 yum install -y java-17-openjdk # 验证 java -version

Ubuntu/Debian系安装Java 17:

# 更新软件源 apt update # 安装JDK apt install -y openjdk-17-jdk # 验证 java -version

安装完成后,用which java看一下路径。这种方式的优点是java命令自动进入PATH,环境变量基本不用管。但如果你想指定某个版本的JDK去跑一个jar,后续还是需要手动定位到具体的JAVA_HOME路径。

2.2 手动解压安装并配置环境变量:生产环境首选

这是我推荐的方式。流程就四步:下载、解压、配置环境变量、验证。关键在于每一步都要知道自己做了什么。

第一步,下载JDK。我一般去Oracle官网或者Adoptium(Eclipse Temurin)下载稳定版,有些网络环境下官网慢,也可以找国内云厂商的镜像站。下载前确认你需要的版本和CPU架构。比如我要在x86_64的Linux上装JDK 17:

# 进入安装目录习惯用的地方 cd /opt # 用wget下载tar.gz包,实际链接以你选择的版本为准 wget https://下载地址/jdk-17_linux-x64_bin.tar.gz

如果服务器上没有wget,用curl -O也可以。下载完成后先校验下文件大小,至少确认不是几KB的错误页面。

第二步,解压到统一目录。我习惯把JDK统一放在/usr/local/java目录下:

mkdir -p /usr/local/java tar -zxvf jdk-17_linux-x64_bin.tar.gz -C /usr/local/java # 看一眼解压出来的目录名 ls /usr/local/java

解压后一般是一个类似jdk-17.0.x这样的目录。为了方便后续升级和路径切换,我还会给它做一个不带版本号的软链接:

ln -s /usr/local/java/jdk-17.0.x /usr/local/java/jdk17

这样环境变量里写/usr/local/java/jdk17,以后JDK小版本更新,只需要调整软链接指向,不用改环境变量。

第三步,配置环境变量。编辑/etc/profile:

vim /etc/profile

在文件末尾追加:

export JAVA_HOME=/usr/local/java/jdk17 export PATH=$JAVA_HOME/bin:$PATH export CLASSPATH=.:$JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar

保存后让配置立即生效:

source /etc/profile

这里解释一下为什么设置JAVA_HOME这么重要。很多中间件,比如Tomcat、Spring Boot的启动脚本,都会直接读取这个变量来找java命令。如果你只改了PATH而没设置JAVA_HOME,就可能出现命令行java -version能显示,但某些工具启动时依然报“Unable to locate a Java Runtime”。

第四步,验证:

java -version javac -version echo $JAVA_HOME

如果屏幕上显示出对应版本号,说明安装成功。

2.3 环境变量配置失败的高频原因

“jdk环境变量配置失败”是搜索热词,说明这个问题太常见了。我踩过或被问过的坑主要有这几个:

  • 忘记执行source /etc/profile,或者新开的SSH会话没有重新登录,导致环境变量不生效。很多初学者容易犯这个错,以为配完就能用。
  • 变量名写错,比如JAVA_HOME拼成JAVAHOME,或者路径中间少了/。配置完建议用env | grep JAVA检查一下。
  • 不小心删掉了PATH中原有的内容。你写export PATH=$JAVA_HOME/bin:$PATH时,$PATH一定不能省,否则连ls、vim这些基础命令都会变得不可用。
  • 在同一台机器上既装了OpenJDK又手动配了Oracle JDK,导致java命令被软链接指向了另一个版本。这种情况下要用type -a java查看所有路径,再把旧的软链接或旧路径从PATH中移除。

提示:如果你在环境中发现ls都不见了,大概率是PATH被覆盖了,紧急恢复办法是直接用绝对路径执行命令,比如/usr/bin/vim /etc/profile,把错误配置改正后重新登录会话。

3. 让jar包用指定的JDK版本运行

多版本JDK共存是非常现实的场景。一台服务器上同时部署老项目和新项目,老项目用Java 8,新项目用Java 17,这不奇怪。下面是我常用的两种做法。

3.1 多版本JDK共存的实际场景

你可能有多个Java应用要部署,而它们依赖的JDK版本不一样。最典型的两种:

  1. 老项目:基于Spring Boot 2.x,需要JDK 8。
  2. 新项目:基于Spring Boot 3.x,强制JDK 17。

如果系统里已经装了一套JDK 8,再装一套JDK 17,两个版本的目录互相独立存放,环境变量只需要指向默认版本。启动具体应用时,用具体JDK的java命令去启动jar包,这就是“指定JDK运行”的核心思路。

比如旧的demo-old.jar必须用JDK 8跑:

/usr/local/java/jdk8/bin/java -jar /opt/app/demo-old.jar

新的demo-new.jar用JDK 17跑:

/usr/local/java/jdk17/bin/java -jar /opt/app/demo-new.jar

这种直接指定绝对路径的方式最直观,也最不容易出错。你不需要关心当前PATH里默认的java是谁,只要确认目标JDK路径存在、权限可执行,就能精确控制。

3.2 处理“找不到jdk”的问题

很多时候jar包启动脚本用的是java命令,而不是绝对路径。如果脚本里写的是java -jar xxx.jar,它找的是PATH里的java。想让脚本找到我们指定的JDK,只需要在运行前临时设置:

export JAVA_HOME=/usr/local/java/jdk17 export PATH=$JAVA_HOME/bin:$PATH java -jar xxx.jar

这种方式在当前Shell会话内有效,关闭会话后失效。如果不想每次手动敲,可以把这些export写进一个启动脚本里,比如:

#!/bin/bash export JAVA_HOME=/usr/local/java/jdk17 export PATH=$JAVA_HOME/bin:$PATH nohup java -jar /opt/app/demo-new.jar &

这样既固定了JDK版本,又保留了一段时间内可手动复用的逻辑。

3.3 更规范的方案:update-alternatives和软链接

在Debian/Ubuntu系统上,可以用update-alternatives管理java版本:

sudo update-alternatives --config java

执行后会出现一个交互式列表,输入数字就能切换系统默认的java。这种方式适合只想整体切换默认java的场景,但如果你要同时运行多个不同JDK的应用,其实还是得回到“脚本里指定绝对路径”这个思路。

软链接的方式同样有效。假设JDK 8和JDK 17都放在/usr/local/java下:

ls -l /usr/local/java/jdk8 /usr/local/java/jdk17

你只需要保证应用启动脚本里JAVA_HOME变量指向对应的软链接,就能在多个版本间切换。这种做法的好处是JDK目录本身不动,只是切换入口,回滚很容易。

4. 部署jar包的完整实操流程

环境好了,接下来才是重头戏:把jar包发到服务器上并跑起来。

4.1 从本地打包到上传服务器

本地如果是Maven项目,打包命令一般是:

mvn clean package -DskipTests

打包完成后,在target目录下会生成一个xxx.jar文件。如果是Spring Boot项目,这个jar是一个可执行jar,内部自带Tomcat等内嵌容器,不需要再装额外的应用服务器。

上传方式我用得最多的有三种:

# 方式一:scp(Linux/Mac本机直接传) scp target/demo-0.0.1-SNAPSHOT.jar root@你的服务器IP:/opt/app/ # 方式二:rz上传(Xshell、SecureCRT里常用,需要先安装lrzsz) yum install -y lrzsz rz # 方式三:在Windows下用WinSCP或FinalShell这类图形工具直接拖拽

小文件用rz最顺手,几百MB以上的大包建议用scp或者图形工具,rz在4G以上文件经常不稳。上传完记得用ls -lh看一下文件大小,确认完整。

4.2 看懂jar包内部结构,才能明白“lib中是什么”

总有人问“jar包的lib中是什么”。这里统一回答:Spring Boot可执行jar里的BOOT-INF/lib目录存放的是项目所有依赖的jar包,包括各种第三方库,比如Spring MVC、Jackson、数据库驱动等。BOOT-INF/classes目录存放的是项目自身编译后的class文件和配置文件。META-INF/MANIFEST.MF文件则记录了程序入口,也就是Main-Class。

我一般用jar tf查看包内容:

jar tf demo-0.0.1-SNAPSHOT.jar | head -20

如果看到BOOT-INF/、META-INF/、org/springframework/boot/loader/这些路径,说明它是一个标准的Spring Boot fat jar。这种jar之所以能直接java -jar运行,关键就在于MANIFEST.MF里指定了启动类,JarFile会在启动时从嵌套的jar中加载类。所以不要试图把BOOT-INF/lib下那些jar解压出来放到classpath里,它们本身就是被组织在fat jar内层的。

如果你是用普通Maven打包,没有配置spring-boot-maven-plugin,那么打出来的jar会是一个普通jar,运行时会提示“no main manifest attribute”,这时需要在pom.xml里配置mainClass,或者改用它对应的上层工具来启动。

4.3 nohup后台启动与日志管理的正确姿势

最常见、也最容易演示的后台运行方式是用nohup配合&:

cd /opt/app nohup java -jar demo-0.0.1-SNAPSHOT.jar --server.port=8080 > /var/log/app/demo-app.log 2>&1 &

解释一下这个命令:

  • nohup让命令忽略挂断信号,保证SSH断开后进程继续跑。
  • &把进程放到后台执行。
  • > /var/log/app/demo-app.log 2>&1把标准输出和错误输出都重定向到指定日志文件。如果不加这个,日志默认写到nohup.out,久了容易乱。

启动后第一件事就是确认进程状态和日志输出:

# 查看Java进程 ps -ef | grep java # 看看端口有没有监听 ss -lntp | grep 8080 # 滚动查看日志,此时能看到Spring Boot启动横幅和端口号 tail -f /var/log/app/demo-app.log

这一步我重点强调:先看日志,再看进程。日志显示“Started DemoApplication in xx seconds”,才是真的起来了。如果日志没动静,或者出现Exception,那么就算有java进程也说明有问题,要立刻去查。

另外,很多人问怎么让进程名字看起来有辨识度,因为用java -jar启动后ps看到的进程都是java,根本无法区分是哪个应用。我的做法是在启动脚本里借助exec -a指定一个任务名:

bash -c 'exec -a demo-app /usr/local/java/jdk17/bin/java -jar demo-0.0.1-SNAPSHOT.jar'

执行后在ps -ef里进程名就会变成demo-app。不过这种方式的稳定性和兼容性在不同系统上有差异,我更推荐直接结合systemd管理,后面会讲。

5. 常见问题排查与运维提升

部署过程中遇到问题是常态,我这里把出现频率最高的几类问题整理出来,可以直接对照排查。

5.1 jar包启动失败的几种典型原因

下表是我在实际排障中反复遇到的,基本可以覆盖90%的启动失败场景:

现象可能原因排查命令或思路
提示 no main manifest attributejar不是Spring Boot可执行jar用jar tf查看META-INF/MANIFEST.MF,检查打包配置
端口被占用另一个进程占用了相同端口ss -lntp | grep 端口号,改端口或杀旧进程
ClassNotFoundExceptionJDK版本不对或依赖缺失确认用对了JDK版本,临时用-d参数看详细启动日志
内存不足服务器内存太小或JVM参数给得太高free -h查看内存,调整-Xmx参数
启动后马上退出数据库连不上、配置文件缺失看tail -100 日志文件,重点搜Caused by
提示 JDK版本错误用了Java 8跑需要Java 17的应用切换JAVA_HOME或直接用绝对路径JDK的java命令

“启动后马上退出”是最常见的坑。很多新手只看到进程没了,没看到日志,就手足无措。我提醒一下:先别急着启动第二次,先把日志文件翻出来看最后一百行,通常真正的报错线索都在Caused by那一行。

5.2 用systemd把Java应用变成系统服务

比nohup更可靠的生产级做法是写一个systemd服务,让应用开机自启、崩溃自动拉起、还可以统一查看日志。项目上线后我首推这种方式。

新建一个服务文件:

vim /etc/systemd/system/demo-app.service

内容如下:

[Unit] Description=Demo Spring Boot Application After=network.target [Service] User=root WorkingDirectory=/opt/app ExecStart=/usr/local/java/jdk17/bin/java -jar /opt/app/demo-0.0.1-SNAPSHOT.jar --server.port=8080 SuccessExitStatus=143 Restart=on-failure RestartSec=5 [Install] WantedBy=multi-user.target

然后执行:

systemctl daemon-reload systemctl enable demo-app systemctl start demo-app systemctl status demo-app

ExecStart里直接写JDK绝对路径,这就又一次落实了“指定JDK运行”的需求。这里的SuccessExitStatus=143是为了兼容Spring Boot优雅停机,普通应用可以根据需要酌情配置。日志查看可以用:

journalctl -u demo-app -f

好处是统一管理,不用自己手动写日志重定向,systemd会自动捕获标准输出。缺点是如果你有特殊日志切割需求,可能还要额外配logrotate。

5.3 几个对面试和日常运维都有用的细节

最后补充几个容易被忽视但很重要的点:

第一个是关于JVM参数。启动jar包时,内存参数是继JDK版本之后最值得关心的项。比如一个常见的启动命令:

nohup java -Xms256m -Xmx1024m -jar demo.jar > startup.log 2>&1 &

-Xmx指定堆上限,-Xms指定初始堆大小。服务器2G内存就别把-Xmx顶到2G,否则系统可能不断发生内存交换。观察可用内存用free -h,观察进程实际占用的物理内存用ps -o pid,rss,cmd -p PID。

第二点是关于kill进程。kill -9是最后的手段,直接强杀可能会导致数据不一致或端口未释放。更规范的做法是先试kill PID,等待几秒,不行再kill -9。线上环境尽量做好优雅停机,配合systemctl stop更好。

第三点是JDK降级问题。热词里有“jdk降级到17”,其实更常见的是从JDK 17降级到JDK 8。这类需求通常是因为某个老应用在本地用高版本编译或运行,但服务器上没有对应版本。切记:版本切换时,贴好JAVA_HOME路径,别贪图方便用软链接乱指,不然会出现你现在以为用的是17,实际进程跑起来用的却是默认8的尴尬局面。

我在实际操作中体会最深的一点是:无论环境多乱,只要两个东西抓住,Java应用就能稳定跑起来。一个是JAVA_HOME到底指到了哪个目录,另一个是启动日志谁在看、怎么看。很多所谓的“怪问题”,最后都不过是这两个基本盘出了问题。如果你第一次部署,可以先把JDK绝对路径和日志文件背后这套逻辑跑通,再往上加systemd、监控都不迟。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/3 3:23:33

写民事执行论文,别一上来就让 AI“替你判案”

适合谁看:公安与司法大类 / 法律执行类 / 民事执行专业,正在准备毕业论文、开题报告或文献综述的同学。 本文用一个很典型的任务串起来:研究“民事执行中被执行人财产查控机制的优化”,最后完成选题、文献综述、案例与规范分析、图…

作者头像 李华
网站建设 2026/10/3 3:22:32

随机森林在零售库存预测中的应用:从特征工程到模型落地

简介:这是一份基于随机森林模型的数据挖掘实战资源包,面向有一定Python基础的初学者或研究者,聚焦零售店库存数据的可视化与预测任务,可直接用于课程设计、毕业设计或项目练习。包内共3个文件:ipynb代码文件涵盖数据清…

作者头像 李华
网站建设 2026/10/3 3:22:27

Qt Graphics View框架实现可拖拽箭头连接:从原理到实战

做这种可拖拽箭头连接的需求,我猜你一定是在做流程图编辑器、拓扑图工具、节点组态软件或者类似的“画布节点连线”交互。最早我拿到这个需求时,第一反应是直接用 QPainter 重绘整个画布,把所有节点和连线都画在一个 widget 上。但实际写到一…

作者头像 李华
网站建设 2026/10/3 3:21:51

大模型数据清洗全攻略:质量过滤、去重与Pandas实操

做模型的人常说一句话:模型能力七分靠数据,三分靠算法。这个说法在大模型时代尤其贴切,因为LLM的训练本质就是“用海量文本压缩人类知识”,如果喂进去的文本本身是垃圾、重复、带毒的,那模型学出来的东西要么是废话&am…

作者头像 李华
网站建设 2026/10/3 3:21:01

实时期货行情 WebSocket 链路实战:从连接到稳定运行

凌晨两点半,多数人已经睡了,我盯着屏幕上滚动的行情帧,心里想的却不是价格本身,而是那条 WebSocket 链路到底还能撑多久。这不是矫情——真实网络环境里,一条看起来"连接正常"的行情通道,很可能早…

作者头像 李华
网站建设 2026/10/3 3:20:57

C#网络应用编程实战:从TCP流到Modbus多设备管理

干我们这行,搞上位机和设备联调的,最常碰到的需求就是“把设备的数据拿回来,处理完再发回去”。这句话听起来简单,真做起来,卡界面、丢包、协议对不上、多台设备相互干扰,能把你一天的耐心消磨干净。这篇文…

作者头像 李华