惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

N
Netflix TechBlog - Medium
罗磊的独立博客
H
Help Net Security
I
Intezer
G
Google Developers Blog
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
T
Troy Hunt's Blog
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
U
Unit 42
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
N
News and Events Feed by Topic
J
Java Code Geeks
S
Security Affairs
T
The Blog of Author Tim Ferriss
Recent Commits to openclaw:main
Recent Commits to openclaw:main
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
D
Docker
The GitHub Blog
The GitHub Blog
F
Full Disclosure
N
News and Events Feed by Topic
Webroot Blog
Webroot Blog
S
Security @ Cisco Blogs
腾讯CDC
人人都是产品经理
人人都是产品经理
M
MIT News - Artificial intelligence
Blog — PlanetScale
Blog — PlanetScale
T
Threatpost
D
DataBreaches.Net
Recent Announcements
Recent Announcements
博客园 - 三生石上(FineUI控件)
MongoDB | Blog
MongoDB | Blog
博客园 - 【当耐特】
L
LINUX DO - 最新话题
Google Online Security Blog
Google Online Security Blog
S
Schneier on Security
S
Securelist
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
Help Net Security
Help Net Security
P
Proofpoint News Feed
Project Zero
Project Zero
S
SegmentFault 最新的问题
H
Hackread – Cybersecurity News, Data Breaches, AI and More
MyScale Blog
MyScale Blog
Google DeepMind News
Google DeepMind News
宝玉的分享
宝玉的分享
Y
Y Combinator Blog
C
CXSECURITY Database RSS Feed - CXSecurity.com
博客园 - 叶小钗

博客园 - 青石路

Claude Code安装,接入阿里云百炼模型,蹭蹭免费额度 异源数据同步 → 记一次 DataX 已同步数据量优化 明明连接的是Redis的DB0,为什么能查到DB3的数据? TINYINT(1) 类型的字段,明明数据存的是 2,为什么查出来是 true 用了MySQL的INSERT ON DUPLICATE KEY UPDATE,怎么还报唯一索引冲突错误 那些年不该放到事务中的操作,你实现过哪些 关于布尔类型的变量不要加 is 前缀,被网友们吐槽了,特来完善下 都说了布尔类型的变量不要加 is 前缀,非要加,这不是坑我了嘛 安全漏洞修复导致SpringBoot2.7与Springfox不兼容,问题排查与处理 记一次SQL隐式转换导致精度丢失问题的排查 → 不规范就踩坑 经由同个文件多次压缩的文件MD5都不一样问题排查,感慨AI的强大! 记一次cannot access its superinterface问题的的排查 → 强如Spring也一样写Bug SpringBoot支持Kafka多源配置的同时还要支持启停配置化,是真的会玩 如果XXL-JOB执行器在执行某任务中被重启了,重启后该任务能够被自动弥补调度吗 Spring Boot读取外部配置文件失败,原因绝对出乎你意料 不依赖 Spring,你会如何自实现 RabbitMQ 消息的消费(一) 异源数据同步 → DataX 同步启动后如何手动终止? 异源数据同步 → 如何获取 DataX 已同步数据量? 记一次 RabbitMQ 消费者莫名消失问题的排查 不升级 POI 版本,如何生成符合新版标准的Excel 2007文件 以MySQL为例,来看看maven-shade-plugin如何解决多版本驱动共存的问题? maven 插件之 maven-shade-plugin,解决同包同名 class 共存问题的神器
记一次多JDK版本问题的排查,一坑套一坑,差点没爬上来
青石路 · 2026-06-30 · via 博客园 - 青石路

开心一刻

一天我在切腊肠,儿子看了看,问我
儿子:爸,这腊肠过几了?过 6 了吧
我:啥叫过 6 了
儿子:这腊肠坏了吧
我憋了好一会,边笑边说:那叫过期,你记错数了吧

不能笑

问题背景

项目中利用 DataX 实现离线同步,并实现了一些自定义的配置,其中 JVM_PARAM用来指定启动 DataX 时的 JVM 参数

java -server -Xms1g -Xmx1g -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=GBK -Ddatax.home=G:\\datax-tool\\datax -Dlogback.configurationFile=G:\\datax-tool\\datax\\conf\\logback.xml -classpath G:\\datax-tool\\datax\\lib\\* com.alibaba.datax.core.Engine -mode standalone -jobid -1 -job G:\\datax-tool\\datax\\job\\job.json

如上 java 命令后的

-Xms1g -Xmx1g -XX:+HeapDumpOnOutOfMemoryError

就是 JVM_PARAM 部分

简化来说:java 命令后的 JVM 参数可配置

项目是基于 JDK8 开发的, JDK8的默认垃圾回收器是 ParallelGC(Parallel Scavenge + Parallel Old),使用者可能觉得垃圾回收性能不行,就想换 G1 ,于是他配置 JVM_PARAM

-Xms1g -Xmx1g -XX:+HeapDumpOnOutOfMemoryError -XX:+UseG1GC

启用 G1 垃圾回收器;结果这个离线同步任务,有时候成功,有时候失败,失败的异常信息如下

Error: VM option 'UseG1GC' is experimental and must be enabled via -XX:+UnlockExperimentalVMOptions.
Error: Could not create the Java Virtual Machine.
Error: A fatal exception has occurred. Program will exit.

问题排查

异常已经提示的很明显了:UseG1GC 是一个实验性参数,必须通过 -XX:+UnlockExperimentalVMOptions 来启用

使用者加上 -XX:+UnlockExperimentalVMOptions 参数后,任务多次执行也不报错了。我本以为事情到此就告一段落了,结果使用者来了一句:为什么有时候成功,有时候失败,你再查查,给我个结论

这么专业的提问,如果我不找到答案,反而显得我不够专业了

从任务的执行情况(有时候成功,有时候失败),再结合失败的异常信息,我得出一个结论

服务部署了多个节点,节点之间存在不同的 JDK 版本,有的版本支持 -XX:+UseG1GC,有的版本不支持单独的 -XX:+UseG1GC

然后我从注册中心确定了该服务一共 2 个节点,结合失败的执行记录(有记录服务器IP),成功锁定执行失败的节点,该节点的 JDK 版本如下

jdk版本

另一个节点的 JDK 版本如下

jdk 1.8.0_41

41 版本支持,292 版本反而不支持?为了保险起见,我写了个 Hello 类,然后在 292 上执行 java -XX:+UseG1GC Hello,结果正常输出,并没有异常

292 Hello

也就是说 292 版本也是支持的,难道不是用 292 版本的 JDK 启动的 DataX ?

正好服务器上有正在执行中的 DataX,我们先切到 root 用户(保证权限足够)

sudo -i

然后找到 datax 进程ID

查datax进程ID

最后确定用的是哪个 java(ls -l /proc/<pid>/exe

java执行文件

用的是 /home/dmdba/jdk1.8.0_371/bin/java,并不是 292 版本!!!

我们用 371 版本执行下 Hello

371_hello

元凶不就找到了嘛

jdk1.8.0_371 不支持单独的 -XX:+UseG1GC

但是问题又来了:为什么 root 用户用的是 371 版本,而 yhuser 用的却是 292 版本?

这其实跟 Linux 的配置文件层级有关

层级 文件位置 生效范围 常见用途
系统全局 /etc/environment 所有用户、所有 Shell(包括图形界面) 设置全局性的基础路径,如 JAVA_HOME(该文件不支持变量引用,纯键值对)
系统全局(登录) /etc/profile/etc/profile.d/*.sh 所有用户登录 Shell 系统级别名、全局 umask,会遍历执行 /etc/profile.d/ 下的脚本
用户专属 ~/.bashrc (非登录)
~/.bash_profile~/.profile (登录)
仅限该用户 用户自定义别名、个人 PATH 追加(如 ~/my_bin

配置文件加载顺序

  1. /etc/environment
  2. /etc/profile
  3. /etc/profile.d/*.sh
  4. ~/.bash_profile 或 ~/.profile
  5. ~/.bashrc,

当同一变量在多个文件出现时,后读取的覆盖先读取的

回到我们的配置,/etc/profile.d/ 下配置的 292 版本(系统全局级别,root 与 yhuser 都能读取到

java292配置

export PATH=$JAVA_HOME/bin:$PATH

root 用户下的 ~/.bash_profile 配置 371 版本(用户层级,root 能读取到,yhuser读取不到

root_java371

export PATH=$JAVA_HOME/bin:$PATH

所以 yhuser 用的是 292 版本;而 root 用户,292 与 371 两个版本都能使用,但因为配置文件的先后加关系,以及 export PATH 的写法,使得 371 在 292 的前面

371在292前面

当执行 java 命令时,会按 $PATH 下的路径从左到右寻找 java 的可执行文件,一旦找到就使用,不会继续往后寻找

因为我们的服务是通过 root 部署的,所以服务通过 java 命令拉起 datax 时,用的自然也是 root 用户,自然而然就从 root 用户的 $PATH 中寻找 java 执行文件了,所以就找到了 371 版本

自此,前因后果是不是都清楚了

问题处理

找到问题了,就好处理了,方法有但不局限于以下几种

  1. -XX+UseG1GC -XX:+UnlockExperimentalVMOptions 一起配置,一起使用

  2. 去掉 root 用户级别的 java 配置

  3. 通过 yhuser 用户启动我们的服务

  4. 服务中加配置,指定 java 命令的绝对路径

    例如:/home/yhuser/app/java/jdk8u292-b10/bin/java -server -Xms1g -Xmx1g -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=GBK -Ddatax.home=G:\datax-tool\datax -Dlogback.configurationFile=G:\datax-tool\datax\conf\logback.xml -classpath G:\datax-tool\datax\lib\* com.alibaba.datax.core.Engine -mode standalone -jobid -1 -job G:\datax-tool\datax\job\job.json

额外补充

后面我确认了 jdk1.8.0_371 版本的 JDK 是 aarch64 架构的

jdk1.8.0_371架构

至于 x86_64 架构的 jdk1.8.0_371 版本支不支持单独的 -XX+UseG1GC,你们去试试

总结

  1. java -version 只能查看当前用户使用的版本,并不代表整个服务器

  2. 查看服务到底使用的是哪个 jdk,可以根据进程ID找到java执行文件

    ls -l /proc//exe

  3. Linux 的配置文件有层级划分,也有优先级规则,当存在多版本时,注意版本间的优先级顺序