Java-216 RocketMQ 4.5.1 在 JDK9+ 从0到1全流程启动踩坑全解:脚本兼容修复(GC 参数/CLASSPATH/ext.dirs)

TL;DR

  • 场景:RocketMQ 4.5.1 在 JDK9+ 启动被「废弃/移除的 JVM 参数」和「类路径未加载 lib」卡住。
  • 结论:删 CMS/ParNew 与 java.ext.dirs,GC 日志切 -Xlog,CLASSPATH 补 ${BASE_DIR}/lib/*。
  • 产出:runserver/runbroker/tools 三处最小改动清单 + 版本矩阵 + 错误速查卡。

版本矩阵

项目 说明
RocketMQ 4.5.1 文中下载与脚本修改均以4.5.1的bin包路径结构为前提(conf、lib、bin)。
JDK 8 不属于本文"必须改脚本"的目标区间;旧参数体系更容易"还能跑",但与本文JDK9+统一日志改法不同。
JDK 9--17 本文修改点(删CMS/ParNew、删java.ext.dirs、-Xlog、补lib/*)面向该区间的典型兼容面。
JDK 18+ 文中未给出该区间的明确验证信号;仍按JDK9+逻辑处理,但不对"零差异可用"做一致性承诺。
Linux(通用) 路径与命令按常见发行版shell环境编写;/etc/profile、/dev/shm、~/logs写法依赖标准目录约定。

RocketMQ 环境搭建

软件下载

我们先进入到目录,然后对软件进行下载:

shell 复制代码
cd /opt/software

接着我们通过 wget 进行下载:

shell 复制代码
wget https://archive.apache.org/dist/rocketmq/4.5.1/rocketmq-all-4.5.1-bin-release.zip

对应内容如下所示:

解压移动

shell 复制代码
unzip rocketmq-all-4.5.1-bin-release.zip
mv rocketmq-all-4.5.1-bin-release ../server/rocketmq

对应的结果如下所示:

修改脚本

  • bin/runserver.sh
  • bin/runbroker.sh
  • bin/tools.sh

我们首先进入配置目录

shell 复制代码
cd /opt/server/rocketmq

runserver

shell 复制代码
vim bin/runserver.sh

根据下面的要求删除对应的内容(后续会解释为什么要删除):

删除

  • UseCMSCompactAtFullCollection
  • UseParNewGC
  • UseConcMarkSweepGC

修改内存

shell 复制代码
JAVA_OPT="${JAVA_OPT} -server -Xms256m -Xmx256m -Xmn128m -XX:MetaspaceSize=64mm -XX:MaxMetaspaceSize=160mm"

修改gc配置

shell 复制代码
-Xloggc 修改为 -Xlog:gc

整体修改完:

shell 复制代码
#===========================================================================================
# JVM Configuration
#===========================================================================================
JAVA_OPT="${JAVA_OPT} -server -Xms256m -Xmx256m -Xmn128m -XX:MetaspaceSize=64m -XX:MaxMetaspaceSize=160m"
JAVA_OPT="${JAVA_OPT} -XX:CMSInitiatingOccupancyFraction=70 -XX:+CMSParallelRemarkEnabled -XX:SoftRefLRUPolicyMSPerMB=0 -XX:+CMSClassUnloadingEnabled -XX:SurvivorRatio=8"
JAVA_OPT="${JAVA_OPT} -verbose:gc -Xlog:gc:/dev/shm/rmq_srv_gc.log -XX:+PrintGCDetails"
JAVA_OPT="${JAVA_OPT} -XX:-OmitStackTraceInFastThrow"
JAVA_OPT="${JAVA_OPT}  -XX:-UseLargePages"
#JAVA_OPT="${JAVA_OPT} -Xdebug -Xrunjdwp:transport=dt_socket,address=9555,server=y,suspend=n"
JAVA_OPT="${JAVA_OPT} ${JAVA_OPT_EXT}"
JAVA_OPT="${JAVA_OPT} -cp ${CLASSPATH}"

classpath 这里需要补一个:

shell 复制代码
# 原来是
export CLASSPATH=".:${BASE_DIR}/conf:CLASSPATH
# 补充完
export CLASSPATH=".:${BASE_DIR}/conf:${BASE_DIR}/lib/*"

基本内容如下所示了:

broker

shell 复制代码
vim bin/runbroker.sh

修改一下JVM的参数:

shell 复制代码
-Xms256m -Xmx256m -Xmn128m

删除

  • PrintGCDateStamps
  • PrintGCApplicationStoppedTime
  • PrintAdaptiveSizePolicy
  • UseGCLogFileRotation
  • NumberOfGCLogFiles=5
  • GCLogFileSize=30m

删除:

shell 复制代码
JAVA_OPT="${JAVA_OPT} -Djava.ext.dirs=${JAVA_HOME}/jre/lib/ext:${BASE_DIR}/lib"

整体上就是删除了一大段:

shell 复制代码
#===========================================================================================
# JVM Configuration
#===========================================================================================
JAVA_OPT="${JAVA_OPT} -server -Xms8g -Xmx8g -Xmn4g"
JAVA_OPT="${JAVA_OPT} -XX:+UseG1GC -XX:G1HeapRegionSize=16m -XX:G1ReservePercent=25 -XX:InitiatingHeapOccupancyPercent=30 -XX:SoftRefLRUPolicyMSPerMB=0"JAVA_OPT="${JAVA_OPT} -verbose:gc -Xloggc:/dev/shm/mq_gc_%p.log -XX:+PrintGCDetails"
JAVA_OPT="${JAVA_OPT} -XX:-OmitStackTraceInFastThrow"
JAVA_OPT="${JAVA_OPT} -XX:+AlwaysPreTouch"
JAVA_OPT="${JAVA_OPT} -XX:MaxDirectMemorySize=15g"
JAVA_OPT="${JAVA_OPT} -XX:-UseLargePages -XX:-UseBiasedLocking"
#JAVA_OPT="${JAVA_OPT} -Xdebug -Xrunjdwp:transport=dt_socket,address=9555,server=y,suspend=n"
JAVA_OPT="${JAVA_OPT} ${JAVA_OPT_EXT}"
JAVA_OPT="${JAVA_OPT} -cp ${CLASSPATH}"

classpath 这里需要补一个:

shell 复制代码
# 原来是
export CLASSPATH=".:${BASE_DIR}/conf:CLASSPATH
# 补充完
export CLASSPATH=".:${BASE_DIR}/conf:${BASE_DIR}/lib/*"

整体如下:

tools

shell 复制代码
vim bin/tools.sh

删除:

shell 复制代码
JAVA_OPT="${JAVA_OPT} -Djava.ext.dirs=${BASE_DIR}/lib:${JAVA_HOME}/jre/lib/ext"

classpath 这里需要补一个:

shell 复制代码
# 原来是
export CLASSPATH=".:${BASE_DIR}/conf:CLASSPATH
# 补充完
export CLASSPATH=".:${BASE_DIR}/conf:${BASE_DIR}/lib/*"

整体结果如下:

针对修改的解释

这些改动的核心动机只有一个:让 RocketMQ 的启动脚本在 JDK9+(尤其 JDK14/15+)不再因为"过时/被移除的 JVM 参数"而告警或直接启动失败,同时把 GC 日志切到 JDK9 引入的统一日志体系。

runserver:删除 CMS/ParNew 相关参数 + 换 GC 日志体系 + 改内存

删除 UseCMSCompactAtFullCollection / UseParNewGC / UseConcMarkSweepGC

  • UseConcMarkSweepGC(CMS)在 JDK9 就被标记为 deprecated,后续版本彻底移除;继续带着该参数会从"警告"变成"直接报错退出"。
  • UseParNewGC 是 CMS 时代常见的年轻代收集器搭配项;当 CMS 被移除后,这类 仅对 CMS 有意义的参数会被废弃/作废。
  • UseCMSCompactAtFullCollection 是 CMS 体系内的 Full GC 压缩控制项;同样属于 CMS 专属参数,CMS 消失后它也没有语义价值。
  • JDK9 起默认 GC 已经切换到 G1("让 G1 成为默认"是 JDK9 的方向之一),所以很多人直接把 CMS/ParNew 参数整段删掉,避免版本兼容问题。

-Xloggc 改为 -Xlog:gc:JDK9 引入 Unified JVM Logging(统一日志框架),GC 日志也随之迁移到 -Xlog 体系;老式的 -Xloggc 与一堆 -XX:+PrintGC... 组合逐步被替代。

修改内存:-Xms/-Xmx/-Xmn/MetaspaceSize/MaxMetaspaceSize

  • -Xms/-Xmx:分别是堆初始/最大;设为相同通常是为了避免运行时扩容带来的抖动(脚本层面追求"固定堆")。
  • -Xmn:年轻代大小(影响 minor GC 频率与停顿形态)。
  • MetaspaceSize/MaxMetaspaceSize:类元数据区阈值与上限;控制类加载相关的内存占用与触发元空间回收的节奏。

broker:删掉一批 GC 日志旧参数(因为统一日志体系不吃这套了)

  • PrintGCDateStamps
  • PrintGCApplicationStoppedTime
  • PrintAdaptiveSizePolicy
  • UseGCLogFileRotation
  • NumberOfGCLogFiles
  • GCLogFileSize

本质都属于 JDK8 及以前的"老 GC 日志开关 + 老日志轮转方式"。

JDK9+ 统一切到 -Xlog 后,这些要么被忽略、要么报 deprecated/obsolete,最终目的是把配置收敛到类似下面的能力集合:

  • 时间戳/停顿/更细粒度标签(由 -Xlog:...:time,uptime,tags,level 之类表达)
  • 文件输出与轮转(在 -Xlog 的 file 语法里完成,而不是 UseGCLogFileRotation/GCLogFileSize/... 这一套老参数)

tools:删除 -Djava.ext.dirs=...(JDK9 起扩展机制被移除)

-Djava.ext.dirs 依赖的是 JRE 的 extension classloader 机制(把 jar 放到 lib/ext 或用 java.ext.dirs 注入)。这个机制在 JDK9 因模块化运行时(JPMS)被移除;只要设置该属性,启动器就可能直接失败。

因此把 tools.sh 里这行删掉,是为了避免在 JDK9+ 上"脚本先天不兼容"的硬错误。

环境变量

配置RocketMQ的环境变量,方便我们后续进行服务启动:

shell 复制代码
vim /etc/profile
shell 复制代码
# RocketMQ 环境变量
export ROCKET_HOME="/opt/server/rocketmq"
export PATH="${ROCKET_HOME}/bin:$PATH"

写入的内容如下所示:

最后记得刷新

shell 复制代码
source /etc/profile

启动服务

NameServer

shell 复制代码
mqnamesrv

查看当前的服务日志:

shell 复制代码
tail -f ~/logs/rocketmqlogs/namesrv.log

Broker

启动服务:

shell 复制代码
mqbroker -n 0.0.0.0:9876

启动结果如下所示:

查看日志:

shell 复制代码
tail -f ~/logs/rocketmqlogs/broker.log

服务测试

发送消息

设置环境变量

shell 复制代码
export NAMESRV_ADDR=0.0.0.0:9876

使用官方提供的Demo:

shell 复制代码
sh bin/tools.sh org.apache.rocketmq.example.quickstart.Producer

执行结果如下所示:

接收消息

设置环境变量

shell 复制代码
export NAMESRV_ADDR=0.0.0.0:9876

使用官方提供的Demo:

shell 复制代码
sh bin/tools.sh org.apache.rocketmq.example.quickstart.Consumer

执行结果如下所示:

关闭RocketMQ

shell 复制代码
mqshutdown namesrv
mqshutdown broker

错误速查

症状 根因定位 修复
Unrecognized VM option 'UseConcMarkSweepGC' 或直接退出 JDK9+ 移除/废弃 CMS/ParNew 体系参数 java -version;启动日志首屏 JVM options 从 runserver.sh 删除 UseConcMarkSweepGC/UseParNewGC/UseCMSCompactAtFullCollection(保持 G1 或默认)
GC 日志参数告警/不生效(-Xloggc、PrintGCDateStamps 等) JDK9+ 统一日志体系迁移到 -Xlog,旧开关逐步废弃 搜索脚本中的 -Xloggc、PrintGC*、UseGCLogFileRotation 将 -Xloggc 改为 -Xlog:gc:...;在 broker 脚本删除老式 GC 日志与轮转参数
Error: Could not find or load main class org.apache.rocketmq.namesrv.NamesrvStartup CLASSPATH 未包含 ${BASE_DIR}/lib/*(或脚本中 CLASSPATH 拼错为字面量 CLASSPATH)导致核心类不在类路径 echo $CLASSPATHbash -x mqnamesrv 看最终 -cp 将 export CLASSPATH=".:${BASE_DIR}/conf:${BASE_DIR}/lib/*" 固化到脚本;避免 ...:CLASSPATH 这种字面量拼接
tools.sh 执行 Demo 报 ClassNotFound / 直接启动失败 -Djava.ext.dirs=... 依赖 JRE ext 机制,JDK9 起移除 tools.sh 搜 java.ext.dirs;观察 JVM 启动报错 删除 JAVA_OPT="${JAVA_OPT} -Djava.ext.dirs=...";用 CLASSPATH=conf + lib/* 承载依赖
JVM 启动时报 Invalid -XX:MetaspaceSize=... 或参数解析失败 参数单位/拼写错误(如 64mm) 启动日志中的 "Improperly specified VM option" 把 -XX:MetaspaceSize=64mm 修正为 -XX:MetaspaceSize=64m(同理检查 MaxMetaspaceSize)
环境变量设置后仍找不到命令 脚本引用目录不一致变量名不一致(如 ROCKETMQ_HOME vs ROCKET_HOME)导致脚本/命令路径判断偏离 envgrep ROCKET;检查 bin 脚本对 HOME 变量的读取

其他系列

🚀 AI篇持续更新中(长期更新)

AI炼丹日志-29 - 字节跳动 DeerFlow 深度研究框斜体样式架 私有部署 测试上手 架构研究 ,持续打造实用AI工具指南!
AI研究-132 Java 生态前沿 2025:Spring、Quarkus、GraalVM、CRaC 与云原生落地
🔗 AI模块直达链接

💻 Java篇持续更新中(长期更新)

Java-207 RabbitMQ Direct 交换器路由:RoutingKey 精确匹配、队列多绑定与日志分流实战

MyBatis 已完结,Spring 已完结,Nginx已完结,Tomcat已完结,分布式服务已完结,Dubbo已完结,MySQL已完结,MongoDB已完结,Neo4j已完结,FastDFS 已完结,OSS已完结,GuavaCache已完结,EVCache已完结,RabbitMQ正在更新... 深入浅出助你打牢基础!
🔗 Java模块直达链接

📊 大数据板块已完成多项干货更新(300篇):

包括 Hadoop、Hive、Kafka、Flink、ClickHouse、Elasticsearch 等二十余项核心组件,覆盖离线+实时数仓全栈!
大数据-278 Spark MLib - 基础介绍 机器学习算法 梯度提升树 GBDT案例 详解
🔗 大数据模块直达链接

相关推荐
num_killer1 天前
小白的Langchain学习
java·python·学习·langchain
期待のcode1 天前
Java虚拟机的运行模式
java·开发语言·jvm
程序员老徐1 天前
Tomcat源码分析三(Tomcat请求源码分析)
java·tomcat
a程序小傲1 天前
京东Java面试被问:动态规划的状态压缩和优化技巧
java·开发语言·mysql·算法·adb·postgresql·深度优先
仙俊红1 天前
spring的IoC(控制反转)面试题
java·后端·spring
阿湯哥1 天前
AgentScope Java 集成 Spring AI Alibaba Workflow 完整指南
java·人工智能·spring
小楼v1 天前
说说常见的限流算法及如何使用Redisson实现多机限流
java·后端·redisson·限流算法
与遨游于天地1 天前
NIO的三个组件解决三个问题
java·后端·nio
czlczl200209251 天前
Guava Cache 原理与实战
java·后端·spring
yangminlei1 天前
Spring 事务探秘:核心机制与应用场景解析
java·spring boot