记一次Dubbo导致的内存泄漏过程分析及解决

本文针对版本机测试环境中Dubbo应用响应缓慢的问题进行了深入排查,最终定位到Zookeeper注册信息缓存文件异常导致的内存泄漏,并通过调整Dubbo配置解决了问题。

       近日测试团队反馈版本机测试环境请求经常卡顿,十分缓慢,甚至有超时的情况,但是请求返回、业务逻辑均是正常的,因此进行了一番排查。

        首先查看应用日志,及控制台监控,应用均表现无异常,由于版本机为单应用测试坏境,因此也排除负载均衡等问题。于是监控了一下linux环境

        1、top命令查询linux环境内存及cpu使用情况,发现应用占用cpu达到了90%多,这是不正常的,一般考虑为是否为频繁fullGC或是程序有死循环导致cpu飙高

         2、top -Hp pid | head -20命令查看该应用最占CPU的各线程情况,果然发现有一个线程占用率高达90%,其余线程均占比个位数或是0

        3、printf '%\x\n' tid  将线程ID转换成16进制,因为日志或是jstack查看时线程ID均是16进制表示的

         4、jstack -l pid |grep spid(16进制) -A  100(输出100行)获取该应用进程内相应线程的堆栈信息,发现该线程为parallelGC线程,为印证是GC导致响应变慢的猜想,在应用启动脚本中加入+PrintGCDetail 打印GC日志,发现果然是在持续的FullGC,然后用jconsole利用JMX端口连接应用,发现年老区占用接近100%,且每次GC后释放内存空间十分有限,这才导致了FullGC

         5、为解决问题,第一反应是JVM内存大小太小,GC收集器可以调整为CMS并发收集器提高响应速度。因此启动脚本中设置提高Xmx,+UseConcMarkSweepGC,并设置年老代占比大于80%后可开始年老代垃圾收集,5次收集后进行compact(CMS基于标记清除,GC后进行压缩减少内存碎片)

          6、配置完成后再查看,年老代基本大小在600M左右,占用率基本维持在75%,倒是看起来没有问题。但是此时我又去查看了一下UAT环境同一个应用的运行情况,发现他的年老代比版本机环境整整少了两三百兆,这就很奇怪了,同样的应用,并发量差不多的情况下,怎么会差别这么大呢?于是果断visualVM将应用内存dump了下来(亦可用jmap -dump)

      7、用memoryAnalyzer导入dump文件进行分析,果然有leak suspects内存泄漏分析报告,首当其冲的是一个zookeeperResigtry类内有一个concurrentHashMap高达200多M 

            8、经分析源码发现dubbo在启动时会对zookeeper上节点进行发布及订阅,会将依赖的producer注册信息缓存在内存中,并会将该信息写入到一个cache文件内,默认在用户根目录下.dubbo文件中,当订阅节点发生变化时亦会读取该文件,全量重写并重新存入内存中。若是linux系统环境下有多个应用均使用dubbo,若不单独设置则均将共用一个.cache文件,不过在写文件时是个原子操作,会生成一个.lock文件进行加锁

              9、本来这也没什么问题,正常.dubbo.cache以我们的接口量也就几十兆最多,但是我们查看版本机dubbo.cache文件时,却发现它高达数百兆,less查看文件信息,发现除了正常的服务注册信息,还有大量的空格、乱码,导致文件巨大。而由于每次zookeeper节点变化都可能导致应用对文件进行读取并重写,因此将导致应用几百兆的内存泄漏

                 10、怀疑是别的dubbo应用在一起写cache文件且应用有问题导致出现乱码、文件巨大,因此,通过阅读dubbo文档,找到了单独配置dubbo.cache文件另立路径的方法,在启动参数中加入-Ddubbo.registry.file=/tmp/dubbo/cache/dubbo.cache,为我们的cache文件单独找了一个去处。重启服务后,果然发现年老代少了200多M,整个堆稳定在了700多M,年老代使用占比45%,运行几天也没再出现过响应卡顿的问题

             可惜暂时还没查出来到底是哪个应用在写dubbo.cache文件异常导致的此问题,且先留待下次分析了

 

<think> 嗯,用户的问题是关于在WebSphere Application Server上运行Dubbo应用时遇到的连接超时、心跳失败和内存溢出问题的解决方案。我需要结合用户提供的引用内容和之前的对话历史来给出全面的回答。 用户提供的引用[1]详细说明了内存溢出的两种主要原因:内存泄漏和内存溢出本身。其中特别提到static修饰过度使用、递归/死循环中的对象创建、数据库全量查询、集合持有对象引用、字符串拼接等问题。这些都是排查JVM崩溃的重要线索。 引用[2]则提供了session共享方案,虽然不直接相关,但其中提到的序列化性能问题和内存数据库的使用思路值得参考。特别是Dubbo的心跳检测和网络通信本身就会涉及序列化操作。 用户的核心痛点有三个层次:网络层(连接超时和心跳失败)、JVM层(内存溢出崩溃)、WAS容器适配层。需要系统性地解决: 首先是网络问题。连接超时要重点检查WAS线程池配置,Dubbo默认的netty传输在WAS上可能需要调整NIO设置。心跳失败往往和TCP keepalive参数或防火墙有关,需要验证端口连通性。Dubbo的心跳线程可能被WAS的安全管理器限制,需要检查was.policy文件。 内存溢出方面,要优先dump堆内存分析。用户引用[1]提到的static滥用问题在Dubbo中常见于ProtocolConfig等全局配置对象。特别要注意Dubbo的Filter链,不当实现会导致内存泄漏。WAS特有的类加载机制也可能造成PermGen溢出,虽然Java 8移除了PermGen,但WAS传统版本仍需关注。 容器适配问题最隐蔽。Dubbo的SPI机制可能和WAS的ParentLast类加载冲突,需要检查是否加载了重复的netty包。WAS的共享库(shared libraries)配置不当会导致类冲突,进而影响心跳线程。JNDI查找超时也可能表现为连接问题。 优化建议需要分层:JVM层调整堆大小和GC策略;Dubbo层压缩传输数据、限制连接数;WAS层调优线程池和类加载。监控方面推荐JDK Mission Control,它能直接关联Dubbo调用链和堆内存对象。 可能延伸的问题是Zookeeper注册中心的稳定性对心跳的影响,以及WAS集群环境下Dubbo直连服务的特殊配置。用户似乎是有经验的系统管理员,可以适当提供高阶调优参数如-XX:+UseContainerSupport等容器感知配置。 </think> 在WebSphere Application Server (WAS) 上解决 Dubbo 应用的连接超时、心跳失败、JVM 崩溃及内存溢出问题,需系统性排查以下关键点: --- ### **一、连接超时与心跳失败解决方案** 1. **网络与端口配置** - **防火墙/安全组**:确认 WAS 服务器与 Dubbo 注册中心(如 ZooKeeper)之间的端口双向开放,避免拦截心跳包(默认端口:20880-20889, 2181) - **WAS 线程池**:检查 WAS 的线程池配置(`服务器 > 线程池`),确保 `最大大小` ≥ Dubbo 的 `dubbo.protocol.threads`(建议 ≥ 200) ```xml <!-- Dubbo 服务提供者配置示例 --> <dubbo:protocol name="dubbo" port="20880" threads="500"/> ``` 2. **Dubbo 超时参数优化** - 增加服务调用超时时间,避免因 WAS GC 暂停导致误判: ```xml <dubbo:consumer timeout="10000"/> <!-- 单位:毫秒 --> <dubbo:provider timeout="10000"/> ``` - 调整心跳间隔(默认 60s),缩短检测失效时间: ```xml <dubbo:protocol heartbeat="30000"/> <!-- 30秒发送一次心跳 --> ``` 3. **TCP 层参数调优** 在 WAS 的 JVM 参数中添加(解决 TCP 半连接问题): ``` -Djava.net.preferIPv4Stack=true -Dcom.ibm.tcp.keepAlive=true -Dsun.net.inetaddr.ttl=300 <!-- 减少 DNS 缓存时间 --> ``` --- ### **二、JVM 崩溃与内存溢出(OOM)解决方案** #### **诊断步骤** 1. **获取内存快照** WAS 崩溃时自动生成 `core.dump` 和 `heapdump`(位置:`${WAS_HOME}/profiles/<profile>/core`),使用 IBM Support Assistant 或 Eclipse MAT 分析。 2. **关键监控指标** ```bash # 监控 WAS JVM 内存 jstat -gcutil <pid> 1000 # 每秒输出 GC 状态 ``` - 关注 `Old Gen (O)` 使用率 ≥ 90% 或频繁 Full GC (FGC) #### **优化措施** 1. **JVM 参数调整** ```bash -Xms4096m -Xmx4096m # 堆大小(根据物理内存调整) -Xmn1024m # 新生代大小(建议 1/4 总堆) -XX:MaxMetaspaceSize=512m # 元空间上限(防 PermGen OOM) -XX:+UseG1GC # 推荐 G1 垃圾收集器 -XX:+HeapDumpOnOutOfMemoryError # OOM 时自动导出堆快照 ``` > **注意**:WAS 默认使用 IBM J9 VM,参数名可能不同(如 `-Xgcpolicy:gencon`) 2. **代码与配置优化** - **静态集合泄漏**:检查全局 `static Map` 是否持续增长(引用[1]) - **递归/循环优化**:避免在循环内创建大对象(如 `new byte[1024*1024]`) - **数据库查询**:分页加载大数据(引用[1]),禁用 `SELECT *` - **序列化优化**:使用 Kryo/FST 替代 Dubbo 默认的 Hessian 序列化,减少内存占用 3. **Dubbo 资源释放** - 确认服务关闭时调用 `Protocol.destroy()` 释放连接 - 限制消费者连接数(防资源耗尽): ```xml <dubbo:reference connections="100"/> ``` --- ### **三、WAS 容器特殊配置** 1. **类加载冲突** 检查是否因 WAS 父子类加载器导致 Dubbo SPI 扩展失效: - 进入 `应用程序 > 应用程序类型 > WebSphere企业应用程序 > [应用] > 类加载顺序` - 选择 **父类最后加载**(Parent Last) 2. **Native 内存泄漏** Dubbo 的 Netty 传输层可能因 Direct Buffer 未释放导致 OOM: ```bash -XX:MaxDirectMemorySize=1024m # 限制堆外内存 ``` 3. **共享库配置** 将 Dubbo 及依赖包(如 Netty、ZooKeeper)添加到 WAS 共享库: ```bash WAS 控制台 > 环境 > 共享库 > 新建 > [库路径] > 关联到应用 ``` --- ### **四、验证与监控** 1. **日志排查** 检查以下日志: - Dubbo 日志:`dubbo-spring.log`(确认心跳状态) - WAS 日志:`SystemOut.log`(搜索 `OutOfMemoryError`) - GC 日志:添加 JVM 参数 `-Xverbosegclog:gc.log` 2. **压力测试** 使用 JMeter 模拟高并发,观察: - 连接失败率(`telnet <IP> 20880`) - 内存增长曲线(JConsole 或 IBM Monitoring and Diagnostic Tools) --- ### **典型问题关联分析** | 现象 | 可能原因 | 解决方案 | |---------------------|----------------------------|-------------------------| | 心跳超时 | WAS 线程池满或 TCP 连接耗尽 | 增大线程池,缩短心跳间隔 | | 消费者调用失败 | 提供者 OOM 崩溃 | 分析堆快照,优化大对象 | | Full GC 频繁 | 内存泄漏或堆空间不足 | 调整 JVM 参数,检查静态集合 | > 引用[1]指出:递归调用、大查询、未释放的对象引用是 OOM 主因。引用[2]中提到的序列化性能问题也与 Dubbo 传输相关,需优化序列化协议[^1][^2]。 --- **
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值