问题

dubbo内部定制的版本中,在处理大于10K的包的时候,会出现内存溢出的现象

原因是我们在定制dubbo http协议的时候,使用了jboss包里面的HttpRequestDecoder的http decoder方法来解析http协议内容

该方法在解析非http协议的大内容时,会出现内存溢出的情况

某个服务因为这个问题,出现了full gc 的情况

复现问题

根据描述复现该问题

  1. 指定dubbo版本
  2. dubbo请求,非http请求
  3. 消息体大于10K

jvm堆配置,jmap -heap pid

  1. Heap Configuration:
  2. MinHeapFreeRatio = 0
  3. MaxHeapFreeRatio = 100
  4. MaxHeapSize = 2147483648 (2048.0MB)
  5. NewSize = 44564480 (42.5MB)
  6. MaxNewSize = 715653120 (682.5MB)
  7. OldSize = 89653248 (85.5MB)
  8. NewRatio = 2
  9. SurvivorRatio = 8
  10. MetaspaceSize = 21807104 (20.796875MB)
  11. CompressedClassSpaceSize = 1073741824 (1024.0MB)
  12. MaxMetaspaceSize = 17592186044415 MB
  13. G1HeapRegionSize = 0 (0.0MB)

构造provider和consumer,然后在consumer的入参约100K,启动tomcat(JVM)的时候添加打印gc详细信息的参数,发起调用的时候观察gc情况

  1. -XX:+PrintGCDetails -XX:+PrintGCDateStamps

第一次调用的时候并没有出现Full GC的情况,想着是不是并发有点小,毕竟线上还是有一定并发量的,所以consumer端启动线程来调用,启动线程个数1、10、100、1000,线程数是1000的时候provider侧出现了FullGC的情况

  1. 2018-01-19T21:01:23.758-0800: [GC (Allocation Failure) [PSYoungGen: 454144K->696K(468992K)] 1394833K->1383056K(1867264K), 0.1343399 secs] [Times: user=0.26 sys=0.01, real=0.14 secs]
  2. 2018-01-19T21:01:23.892-0800: [Full GC (Ergonomics) [PSYoungGen: 696K->0K(468992K)] [ParOldGen: 1382359K->940805K(1398272K)] 1383056K->940805K(1867264K), [Metaspace: 52098K->52098K(1097728K)], 0.2305879 secs] [Times: user=0.54 sys=0.01, real=0.23 secs]
  3. 2018-01-19T21:01:24.629-0800: [Full GC (Ergonomics) [PSYoungGen: 464621K->0K(468992K)] [ParOldGen: 1382601K->941472K(1398272K)] 1847223K->941472K(1867264K), [Metaspace: 52098K->52098K(1097728K)], 0.2063340 secs] [Times: user=0.59 sys=0.00, real=0.21 secs]
  4. 2018-01-19T21:01:25.305-0800: [Full GC (Ergonomics) [PSYoungGen: 454693K->0K(468992K)] [ParOldGen: 1383395K->499265K(1398272K)] 1838088K->499265K(1867264K), [Metaspace: 52098K->52098K(1097728K)], 0.1478104 secs] [Times: user=0.34 sys=0.01, real=0.15 secs]
  5. 2018-01-19T21:01:25.945-0800: [Full GC (Ergonomics) [PSYoungGen: 457504K->0K(468992K)] [ParOldGen: 1383424K->499950K(1398272K)] 1840928K->499950K(1867264K), [Metaspace: 52098K->52098K(1097728K)], 0.1390671 secs] [Times: user=0.36 sys=0.00, real=0.14 secs]
  6. 2018-01-19T21:01:26.585-0800: [Full GC (Ergonomics) [PSYoungGen: 456673K->0K(468992K)] [ParOldGen: 1384488K->499639K(1398272K)] 1841162K->499639K(1867264K), [Metaspace: 52098K->52098K(1097728K)], 0.1344279 secs] [Times: user=0.32 sys=0.01, real=0.14 secs]

jstat -gc pid -t 1s

  1. Timestamp S0C S1C S0U S1U EC EU OC OU MC MU CCSC CCSU YGC YGCT FGC FGCT GCT
  2. 1438.8 1024.0 5120.0 0.0 0.0 443392.0 430848.7 1398272.0 478878.3 53464.0 52117.3 6144.0 5945.4 1733 97.928 949 166.820 264.748
  3. 1440.0 1024.0 5120.0 0.0 0.0 443392.0 435843.6 1398272.0 1321492.8 53464.0 52117.3 6144.0 5945.4 1733 97.928 950 166.962 264.889
  4. 1441.0 1024.0 5120.0 0.0 0.0 443392.0 433460.4 1398272.0 900800.9 53464.0 52117.3 6144.0 5945.4 1733 97.928 952 167.226 265.153
  5. 1441.9 1024.0 5120.0 0.0 0.0 443392.0 0.0 1398272.0 479419.0 53464.0 52117.3 6144.0 5945.4 1733 97.928 954 167.491 265.419
  6. 1443.0 1024.0 5120.0 0.0 0.0 443392.0 438270.0 1398272.0 1324328.4 53464.0 52117.3 6144.0 5945.4 1733 97.928 955 167.632 265.560
  7. 1444.0 1024.0 5120.0 0.0 0.0 443392.0 437239.3 1398272.0 902696.2 53464.0 52117.3 6144.0 5945.4 1733 97.928 957 167.902 265.830
  8. 1445.0 1024.0 5120.0 0.0 0.0 443392.0 440249.7 1398272.0 1326030.9 53464.0 52117.3 6144.0 5945.4 1733 97.928 959 168.046 265.974
  9. 1446.0 1024.0 5120.0 0.0 0.0 443392.0 434997.3 1398272.0 903830.7 53464.0 52117.3 6144.0 5945.4 1733 97.928 960 168.341 266.269
  10. 1447.0 1024.0 5120.0 0.0 0.0 443392.0 423591.6 1398272.0 480931.8 53464.0 52117.3 6144.0 5945.4 1733 97.928 962 168.610 266.537

问题原因

发生了FullGC,先拿出现在的Heap Dump看看当前JVM的堆内存使用情况

  1. jmap -dump:format=b,file=dump.bin pid

拿到dump文件,拿到MAT中分析下,在dominator_tree视图中找到占用内存较多的对象,这里是byte数组,接下来找到byte数组属于哪一个类,在byte数据上右键"Path To GC Roots" -> "with all reference"就是下图

看到org.jboss.netty.handler.codec.frame.FrameDecoder#cumulation这个对象很大,这个类是HttpRequestDecoder的超类,接下来就是调试源码,查看为什么这个字段会这么大

找到这个字段的所有引用的地方,查看哪里往byte数组(cumulation这个字段包含一个byte数组)中写,主要是下面两个方法

  1. // 这个方法只是把原来的cumulation和新增的input复合到一个对象中,CompositeChannelBuffer
  2. org.jboss.netty.handler.codec.frame.FrameDecoder#appendToCumulation
  3. // 这个方法会对cumulation重新赋值,并把input写入cumulation中,也就是这个时候byte数组会增大
  4. org.jboss.netty.handler.codec.frame.FrameDecoder#updateCumulation

org.jboss.netty.handler.codec.replay.ReplayingDecoder#messageReceived方法中调用上面这两个方法,ReplayingDecoder是HttpRequestDecoder的超类,在接收到请求的时候会调用messageReceived方法,所以在接收到请求的时候就会向cumulation中写数据。

那么现在问题基本清晰了,不断往byte数组中写数据导致byte数组不断增大,问题应该出在byte数组没有被清空上,所以现在的问题是

为什么cumulation没有被清空?

查找所有cumulation被清空的方法

  1. org.jboss.netty.handler.codec.replay.ReplayingDecoder#cleanup

看看谁调用了这个方法

  1. // 连接断开的时候调用这个方法
  2. org.jboss.netty.handler.codec.frame.FrameDecoder#channelDisconnected
  3. // 连接关闭的时候调用这个方法
  4. org.jboss.netty.handler.codec.frame.FrameDecoder#channelClosed

所以只有在连接断开或者关闭的时候才会清空cumulation,结论已经呼之欲出了,回到一开始出现的问题

  1. dubbo请求,非http请求
  2. 大量并发请求

在接收到dubbo请求的时候,虽然不是http协议,但是还是会执行HttpRequestDecoder这个handler,也就会往cumulation中写数据,但是dubbo协议使用的是长连接(netty维护的长连接),所以dubbo请求的内容都会被加入到cumulation中,直到连接关闭或者断开才会清空,如果并发量大的话就会导致有多个cumulation大对象,如果对空间不够的时候就会引起FullGC。

而http协议一般都是短连接,或者有超时时间(服务端超时或者客户端超时),这样cumulation就会被及时清空,所以http协议不容易出现这种问题,而dubbo协议更容易出现的原因。

解决问题

既然是非http请求就不需要使用HttpRequestDecoder做进一步处理,所以新建一个类HttpRequestWrapDecoder继承自HttpRequestDecoder,重写messageReceived方法,在该方法里面判断是否是http请求,如果是http请求才会进一步处理,否则直接返回不处理,执行pipeline中下一个handler

dubbo扩展http协议后FullGC的更多相关文章

  1. swift学习笔记4——扩展、协议

    之前学习swift时的个人笔记,根据github:the-swift-programming-language-in-chinese学习.总结,将重要的内容提取,加以理解后整理为学习笔记,方便以后查询 ...

  2. Dubbo支持的协议的详解

    Dubbo支持dubbo.rmi.hessian.http.webservice.thrift.redis等多种协议,但是Dubbo官网是推荐我们使用Dubbo协议的.下面我们就针对Dubbo的每种协 ...

  3. 精通Dubbo——Dubbo支持的协议的详解

    转: 精通Dubbo——Dubbo支持的协议的详解 2017年06月02日 22:26:57 孙_悟_空 阅读数:44500   Dubbo支持dubbo.rmi.hessian.http.webse ...

  4. Dubbo 使用rest协议发布http服务

    演示用GitHub地址:https://github.com/suyin58/dubbo-rest-example 1       Dubbo_rest介绍 Dubbo自2.6.0版本后,合并了dub ...

  5. 从Spring容器的角度理解Dubbo扩展点的加载时机

    对于Dubbo提供的扩展点,主程序执行的过程中并没有显示调用加载的过程,无论是自激活的Filter还是自适应的ThreadPool.那么这样的扩展点在程序运行的哪个节点调用的呢?跟踪之前性能监控扩展点 ...

  6. IOS中类的扩展(协议,分类)

    IOS中类的扩展(协议,分类) 扩展类,我们可以使用协议和分类这两种方法,下面我们来分别实现这两种方法: 参考网址:http://www.cnblogs.com/wendingding/p/37095 ...

  7. swift3.0 扩展、协议(4)

    扩展和协议是swift中的两个特性,用于对已有的类型进行扩展和修改. 扩展(extension) 向已经存在的类型添加新的功能(属性.方法.下标脚本等等),扩展使用extension关键字定义,语法 ...

  8. 采用truelicense进行Java规划license控制 扩展可以验证后,license 开始结束日期,验证绑定一个给定的mac住址

    采用truelicense进行Java规划license控制 扩展可以验证后,license 开始结束日期,验证绑定一个给定的mac住址. Truelicense 它是一个开源java license ...

  9. [转载] 基于Dubbo的Hessian协议实现远程调用

    转载自http://shiyanjun.cn/archives/349.html Dubbo基于Hessian实现了自己Hessian协议,可以直接通过配置的Dubbo内置的其他协议,在服务消费方进行 ...

随机推荐

  1. 解决:java.io.IOException: No FileSystem for scheme: hdfs

    解决:java.io.IOException: No FileSystem for scheme: hdfs 开发项目初期,写完代码开始放到服务器上开始测试的时候,报出这样的一个错,不知道怎么处理了, ...

  2. RHM-M60型挖掘机力矩限制器/载荷指示器

    RHM-M60挖掘机力矩限制器RHM-M60 excavator crane moment limiter     RHM-M60型挖掘机力矩限制器是臂架型起重机机械的安全保护装置,本产品采用32位高 ...

  3. Java与算法之(3) - 斐波那契数列

    斐波那契数列问题:如果一对兔子每月能生1对小兔子,而每对小兔在它出生后的第三个月里,又能开始生1对小兔子,假定在不发生死亡的情况下,由一对初生的兔子开始,1年后能繁殖出多少对兔子? 首先手工计算来总结 ...

  4. Linux下安装opencv模块

    最近微信上流行的给自己的头像加一顶圣诞帽,想用python写一个程序自己实现一下,其中需要用到opencv import cv2 现在记录一下如何在Linux系统(ubutun)下安装该模块: 参考了 ...

  5. CTF---Web入门第十四题 忘记密码了

    忘记密码了分值:20 来源: Justatest 难度:中 参与人数:7706人 Get Flag:2232人 答题人数:2386人 解题通过率:94% 找回密码 格式:SimCTF{ } 解题链接: ...

  6. bzoj:1230: [Usaco2008 Nov]lites 开关灯

    Description Farmer John尝试通过和奶牛们玩益智玩具来保持他的奶牛们思维敏捷. 其中一个大型玩具是牛栏中的灯. N (2 <= N <= 100,000) 头奶牛中的每 ...

  7. CodeM美团点评编程大赛初赛B轮 黑白树【DFS深搜+暴力】

    [编程题] 黑白树 时间限制:1秒 空间限制:32768K 一棵n个点的有根树,1号点为根,相邻的两个节点之间的距离为1.树上每个节点i对应一个值k[i].每个点都有一个颜色,初始的时候所有点都是白色 ...

  8. Frame Stacking(拓扑排序)

    题目链接:http://acm.tju.edu.cn/toj/showp1076.html1076.   Frame Stacking Time Limit: 1.0 Seconds   Memory ...

  9. An Easy Problem?!(细节题,要把所有情况考虑到)

    http://poj.org/problem?id=2826 An Easy Problem?! Time Limit: 1000MS   Memory Limit: 65536K Total Sub ...

  10. 如何给虚拟主机安装phpMyAdmin

    很多虚拟主机没有phpMyAdmin,例如阿里云的云虚拟主机默认的数据库管理工具是DMS,这样好多朋友管理数据库时会觉得不方便.phpMyAdmin是比较大众和常用的Mysql数据库管理软件,我们可以 ...