RocketMQ:底层Netty频繁OS OOM

简介: JVM使用jemalloc分配native内存,排查发现无明显泄漏。通过pmap、gdb分析及Arthas查看Netty堆外内存占用,定位到RocketMQ客户端因Netty直接内存分配导致内存超1G。调整MaxDirectMemorySize引发消息积压,最终确认问题源于Netty内存管理机制。短期方案为压缩Java堆,腾出堆外空间保障MQ可用性。

1.2.native内存泄漏了吗
JVM使用什么native分配器
通过查看机器上安装的JDK的信息,可以看到使用的是jemalloc的内存分配器。是不是它有泄漏、内存碎片、归还不及时的问题?
网上搜索,发现的有一篇文章讲的场景和我们这里的有一些类似:链接
尝试重新下载jemalloc的源码,并进行其参数的调整:
export MALLOC_CONF="dirty_decay_ms:0,muzzy_decay_ms:0"
观察发现内存的占用量有少量的下降,但还是会超过1个G,看起来核心问题不在这里。
谁在分配内存
同时还通过perf工具监控了下调用内存分配的调用栈,想看看有什么线索没有,然而并没有什么线索。毕竟这个内存的增长比较缓慢,perf也不可能抓太长时间了,遂放弃这个思路。
sudo perf probe -x /opt/taobao/install/ajdk11_11.0.23.24/lib/libjemalloc.so.2 malloc
sudo perf record -e probe_libjemalloc:malloc -p pidof java -g -- sleep 10

内存里面装了什么
通过 sudo pmap -x pidof java | sort -k 3 -n 命令查看进程的所有内存块信息,如下图示:

排除最大的4G的这一个(这是Java堆),以及内存标志带x的两个(可执行代码标志,那是CodeCache),把其它的块都dump下来,看看里面都放了啥,有没有什么不平凡的。
使用gdb命令:gdb --batch --pid pidof java -ex "dump memory mem1.log 0x7f0109800000 0x7f0109800000+0x200000"
然后将dump下的内存以字符串的方式输出观察下:cat mem1.log | strings

如图所示,发现里面大量的内容都和RocketMQ有关。不过我发现我草率了,这些dump内容我看了快一天,根本没有发现什么不太对的地方,看起来都是正常的占用。(不过明显能看出来这里面存了一堆消费者信息,表达的比较冗余)
求助JVM专家
还真是从入门到放弃,到这个时候已经没啥信心啦。遂求助于JVM的专家毛亮,他给了大的方向,一是这里不太可能有native的内存泄漏,二是既然怀疑是堆外,把堆外内存减少一点看看情况,明确下是不是native内存分配器的回收特性就是这样。往往native的内存分配器都有自己的管理策略,他会有自己的回收拐点,比应用看到的高一点是合理的。
的确,那么接下来的策略就是把MaxDirectMemeorySize调低到512M观察下效果吧。
1.3.堆外内存调小影响业务了
在堆外内存从1G调小到512M过后,过了个周末,周一的时候业务同学就反馈,调小遇到问题了,存在MQ消息消费不及时而导致消息挤压的问题。结合之前看到的native内存的信息,突然想到,MQ客户端一定是占用了超过512M的内存,内心里出现了两个问题:
1.MQ底层依赖netty,那么netty实际使用的内存是多少?以及这个内存占用量和native的堆外占用量是什么关系?
2.为啥Java的DirectMemory占用这么少,netty的内存占用似乎并没有被看到,这是怎么回事?
带着这两个问题,查看了netty内存管理的核心类 io.netty.buffer.PooledByteBufAllocator,以及机器上启动过程中打印出的信息。

结合这里面涉及的另一个核心类io.netty.util.internal.PlatformDependent,大概明白了这里面的逻辑,netty是直接使用(是有前提条件的,但这个应用通过JVM参数[-Dio.netty.tryReflectionSetAccessible=true]开启了这个特性,这也是大多数应用上面的行为)UNSAFE.allocateMemory分配内存,完全绕过Java的直接内存API。然后它自己实现了内存占用空间的限制,这个值等于JVM参数中的MaxDirectMemorySize。到这里,似乎发现了曙光,莫非就是netty?(netty这么做的原因是为了不依赖JVM机制而加速内存的释放,同时也是为了解决在堆外内存不足时JVM的糟糕的回收机制设计。)
1.4.Netty到底占用了多少内存
好在netty的类中有一个静态变量是可以很容易的看到这个信息的:
io.netty.buffer.PooledByteBufAllocator#DEFAULT。
那么这个时候就是需要上机器去执行它了。Arthas是个不错的工具,可以直接在机器执行表达式看任何静态变量的值,并不需要我们改代码然后去调用上面的对象做日志打印。
登录机器后,通过命令查找netty Allocator的类定义:
sc -d io.netty.buffer.PooledByteBufAllocator

发现有不止一个Allocator,来自于不同的ClassLoader,以及不同的jar包。一共有7个。
然后一个一个的看他们实际占用的大小:
getstatic -c d5bc00 io.netty.buffer.PooledByteBufAllocator DEFAULT

然后把他们占用的内存逐项加起来,发现的确超过了1G,同时和前面通过NMT看到的Other类别的内存大小是比较吻合的。到这里大概就明确具体是怎么回事了,内存是netty用掉的。
1.5.业务应该怎么做呢
到目前为此,问题是明确了,但似乎并没有什么太好的解法。一个是rocketmq-client的内存占用是不是太大了,有没有什么可以优化的地方?(从前面看native内存看到的内容来看,还是有很大的优化空间的,一大堆地址信息都是以字符串的形式写在内存里面),另一个是中间件的调整肯定是长期的,短期业务要怎么办呢?
思考再三,短期来看只能是先让业务把Java堆调小(通过Java dump以及JVM监控可以看出来堆的使用率并不高),来适应当前的现状了【调整Java堆,给堆外更多的内存空间,以保证MQ的高可用】。

相关文章
|
4月前
|
消息中间件 存储 Java
企业实战RocketMQ:从API到架构开发的深度解析与落地实践
本文全面介绍了Apache RocketMQ消息中间件的核心技术与实战应用。首先解析了RocketMQ的四大核心组件(NameServer、Broker、Producer、Consumer)及其底层逻辑,包括路由发现机制和三层存储结构。接着详细演示了环境搭建、API开发(普通/顺序/批量/事务消息)、企业级架构设计(高可用集群、消息可靠性保障)和幂等性处理方案。最后提供了常见问题排查方法和性能优化建议,涵盖Broker配置、生产消费优化等关键点。所有示例代码均经过生产验证,可直接应用于实际项目开发。
308 2
|
消息中间件 存储 监控
自顶向下学习 RocketMQ(十):消息重投和消息重试
生产者在发送消息时,同步消息失败会重投,异步消息有重试,oneway 没有任何保证。消息重投保证消息尽可能发送成功、不丢失,但可能会造成消息重复,消息重复在 RocketMQ 中是无法避免的问题。消息重复在一般情况下不会发生,当出现消息量大、网络抖动,消息重复就会是大概率事件。另外,生产者主动重发、consumer 负载变化也会导致重复消息。
自顶向下学习 RocketMQ(十):消息重投和消息重试
|
3月前
|
存储 人工智能 搜索推荐
向量数据库的基本概念
向量数据库是专为存储和检索高维向量设计的系统,能将图片、文本等非结构化数据转化为“数字指纹”(向量),通过相似性搜索快速找到相近内容,广泛应用于推荐系统、图像识别和AI搜索等领域。
|
11月前
|
消息中间件 大数据 关系型数据库
RocketMQ实战—3.基于RocketMQ升级订单系统架构
本文主要介绍了基于MQ实现订单系统核心流程的异步化改造、基于MQ实现订单系统和第三方系统的解耦、基于MQ实现将订单数据同步给大数据团队、秒杀系统的技术难点以及秒杀商详页的架构设计和基于MQ实现秒杀系统的异步化架构。
734 64
RocketMQ实战—3.基于RocketMQ升级订单系统架构
|
3月前
|
人工智能 JSON 数据挖掘
全面认识MCP:大模型连接真实世界的“USB-C接口”
MCP通过动态上下文窗口、多步骤流程支持与标准化通信协议,实现AI智能体对用户偏好、会话历史与环境数据的持续记忆与灵活响应。其基于JSON-RPC 2.0的统一接口,支持Stdio、HTTP/SSE等传输方式,简化了大模型与工具系统的集成。借助MCP,AI应用可高效完成数据分析、办公自动化等复杂任务,提升处理能力的同时保障安全合规,推动大模型在真实场景中的落地应用。
|
3月前
|
人工智能 自然语言处理 API
全面认识MCP:大模型连接真实世界的“USB-C接口”
MCP解决AI工具集成难题,打破“工具孤岛”。通过标准化协议,实现模型与工具的即插即用,降低开发成本,提升AI连接现实世界的能力。
|
3月前
|
存储 机器学习/深度学习 人工智能
向量数据库的工作原理
向量数据库通过将非结构化数据转化为高维向量嵌入,利用HNSW、IVF-PQ等索引技术实现高效相似性搜索。其采用列式存储、量化压缩与分布式架构,优化高维向量的存储与检索,支持AI场景下的大规模近似最近邻查询,显著提升搜索效率与可扩展性。
|
消息中间件 C语言 RocketMQ
消息队列 MQ操作报错合集之出现"Connection reset by peer"的错误,该如何处理
消息队列(MQ)是一种用于异步通信和解耦的应用程序间消息传递的服务,广泛应用于分布式系统中。针对不同的MQ产品,如阿里云的RocketMQ、RabbitMQ等,它们在实现上述场景时可能会有不同的特性和优势,比如RocketMQ强调高吞吐量、低延迟和高可用性,适合大规模分布式系统;而RabbitMQ则以其灵活的路由规则和丰富的协议支持受到青睐。下面是一些常见的消息队列MQ产品的使用场景合集,这些场景涵盖了多种行业和业务需求。
|
消息中间件 架构师 Apache
一本书精通Apache RocketMQ
一本书精通Apache RocketMQ
430 3
|
消息中间件 存储 缓存
【Alibaba中间件技术系列】「RocketMQ技术专题」Broker服务端自动创建topic的原理分析和问题要点指南
【Alibaba中间件技术系列】「RocketMQ技术专题」Broker服务端自动创建topic的原理分析和问题要点指南
2025 84
【Alibaba中间件技术系列】「RocketMQ技术专题」Broker服务端自动创建topic的原理分析和问题要点指南