org.apache.thrift导致的OOM

本文探讨了一次微服务压测中遇到的异常OOM问题,源于Thrift框架的string类型反序列化时创建的大数组。通过分析Java堆空间不足和源码定位,作者揭示了问题原因,并提供了通过配置限制字符串长度来避免oom的方法。

1. 背景

昨日开始压测某微服务,但是发现了一个很奇怪的OOM: java heap space

  • 通过压测平台压测必现,即使QPS=1时也必现
  • 手动触发无法重现
  • 发生OOM时,GC日志显示java heap并未全部占用,但gc触发原因是GC (Allocation Failure)
  • 除了OOM日志,没有其他任何拦截器日志和业务日志

虽然已经在生产环境见过很多种OOM,但这类奇怪的OOM还是第一次见到。

2. 分析过程

2.1 Java Dump

分析OOM第一步通常就是查看dump文件,但是由于压测环境隔离等原因,至今无法拿到dump文件,先跳过这一步。

2.2 源码

查看错误日志,发现OOM每次都出现在TBinaryProtocol类readStringBody方法的同一行,代码如下:

  public String readStringBody(int size) throws TException {
    try {
      // OOM每次都发生在下行
      byte[] buf = new byte[size];
      trans_.readAll(buf, 0, size);
      return new String(buf, "UTF-8");
    } catch (UnsupportedEncodingException uex) {
      throw new TException("JVM DOES NOT SUPPORT UTF-8");
    }
  }

很明显,OOM是由于反序列化字符串时创建了一个大数组导致的。

2.3 验证猜想

修改配置文件添加thrift.server.protocol.stringLengthLimit = 1048576 1MB,重新发布后。触发压测脚本,错误日志变成了:

org.apache.thrift.protocol.TProtocolException: String length exceeded max allowed: 1347375956; stringlength limit: 1048576
at org.apache.thrift.protocol.TBinaryProtocol.readMessageBegin(TBinaryProtocol.java:230)
at org.apache.thrift.TBaseProcessor.process(TBaseProcessor.java:27)
at org.apache.thrift.server.TThreadPoolServer$WorkerProcess.run(TThreadPoolServer.java:286)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
at java.lang.Thread.run(Thread.java:748)

Thrift框架试图new一个1347375956长度的byte数组,导致OOM。

3. 原理分析

3.1 Thrift Binary序列化规范

在这里插入图片描述
由上表可知,string类型占用字节数为4+N,前4位无符号标识字符串长度,反序列化时,会根据该值分配byte数组。

4. 引用

Thrift序列化协议浅析

176825 [HiveServer2-Handler-Pool: Thread-47] DEBUG org.apache.thrift.transport.TSaslTransport - writing data length: 42 176900 [HiveServer2-Handler-Pool: Thread-47] DEBUG org.apache.thrift.transport.TSaslTransport - SERVER: reading data length: 85 176900 [HiveServer2-Handler-Pool: Thread-47] DEBUG org.apache.hadoop.security.UserGroupInformation - PrivilegedAction [as: dashuju (auth:PROXY) via dashuju (auth:SIMPLE)][action: org.apache.hive.service.cli.session.HiveSessionProxy$1@54fa2166] java.lang.Exception at org.apache.hadoop.security.UserGroupInformation.doAs(UserGroupInformation.java:1875) at org.apache.hive.service.cli.session.HiveSessionProxy.invoke(HiveSessionProxy.java:59) at com.sun.proxy.$Proxy39.getInfo(Unknown Source) at org.apache.hive.service.cli.CLIService.getInfo(CLIService.java:252) at org.apache.hive.service.cli.thrift.ThriftCLIService.GetInfo(ThriftCLIService.java:541) at org.apache.hive.service.rpc.thrift.TCLIService$Processor$GetInfo.getResult(TCLIService.java:1537) at org.apache.hive.service.rpc.thrift.TCLIService$Processor$GetInfo.getResult(TCLIService.java:1522) at org.apache.thrift.ProcessFunction.process(ProcessFunction.java:39) at org.apache.thrift.TBaseProcessor.process(TBaseProcessor.java:39) at org.apache.hive.service.auth.TSetIpAddressProcessor.process(TSetIpAddressProcessor.java:56) at org.apache.thrift.server.TThreadPoolServer$WorkerProcess.run(TThreadPoolServer.java:286) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) at java.lang.Thread.run(Thread.java:748)
05-26
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值