问题背景项目中利用DataX实现离线同步并实现了一些自定义的配置其中JVM_PARAM用来指定启动 DataX 时的 JVM 参数shell代码解读复制代码java -server -Xms1g -Xmx1g -XX:HeapDumpOnOutOfMemoryError -Dfile.encodingGBK -Ddatax.homeG:\\datax-tool\\datax -Dlogback.configurationFileG:\\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的默认垃圾回收器是ParallelGCParallel Scavenge Parallel Old使用者可能觉得垃圾回收性能不行就想换G1于是他配置 JVM_PARAM-Xms1g -Xmx1g -XX:HeapDumpOnOutOfMemoryError -XX:UseG1GC启用 G1 垃圾回收器结果这个离线同步任务有时候成功有时候失败失败的异常信息如下java代码解读复制代码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 版本如下41 版本支持292 版本反而不支持为了保险起见我写了个 Hello 类然后在 292 上执行java -XX:UseG1GC Hello结果正常输出并没有异常也就是说 292 版本也是支持的难道不是用 292 版本的 JDK 启动的 DataX 正好服务器上有正在执行中的 DataX我们先切到 root 用户保证权限足够sudo -i然后找到 datax 进程ID最后确定用的是哪个 javals -l /proc/pid/exe用的是/home/dmdba/jdk1.8.0_371/bin/java并不是 292 版本我们用 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配置文件加载顺序/etc/environment/etc/profile/etc/profile.d/*.sh~/.bash_profile 或 ~/.profile~/.bashrc当同一变量在多个文件出现时后读取的覆盖先读取的回到我们的配置/etc/profile.d/下配置的 292 版本系统全局级别root 与 yhuser 都能读取到export PATHJAVAHOME/bin:JAVA_HOME/bin:JAVAH​OME/bin:PATHroot 用户下的~/.bash_profile配置 371 版本用户层级root 能读取到yhuser读取不到export PATHJAVAHOME/bin:JAVA_HOME/bin:JAVAH​OME/bin:PATH所以 yhuser 用的是 292 版本而 root 用户292 与 371 两个版本都能使用但因为配置文件的先后加关系以及 export PATH 的写法使得 371 在 292 的前面当执行 java 命令时会按 $PATH 下的路径从左到右寻找 java 的可执行文件一旦找到就使用不会继续往后寻找因为我们的服务是通过 root 部署的所以服务通过 java 命令拉起 datax 时用的自然也是 root 用户自然而然就从 root 用户的 $PATH 中寻找 java 执行文件了所以就找到了 371 版本自此前因后果是不是都清楚了问题处理找到问题了就好处理了方法有但不局限于以下几种-XXUseG1GC -XX:UnlockExperimentalVMOptions 一起配置一起使用去掉 root 用户级别的 java 配置通过 yhuser 用户启动我们的服务服务中加配置指定 java 命令的绝对路径例如/home/yhuser/app/java/jdk8u292-b10/bin/java -server -Xms1g -Xmx1g -XX:HeapDumpOnOutOfMemoryError -Dfile.encodingGBK -Ddatax.homeG:\datax-tool\datax -Dlogback.configurationFileG:\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作者青石路链接https://juejin.cn/post/7657064874854760494来源稀土掘金著作权归作者所有。商业转载请联系作者获得授权非商业转载请注明出处。