1. 首页
  2. IT资讯

惊:Dubbo居然有必现StackOverflowError的Bug

“u003Cdivu003Eu003Cblockquoteu003E转载:https:u002Fu002Fmp.weixin.qq.comu002Fsu002FVUUdNT-MeNSQilpqeR9CVw作者:阿飞的博客u003Cu002Fblockquoteu003Eu003Cp class=”ql-align-justify”u003Eu003Cstrongu003E说明u003Cu002Fstrongu003E:本文场景基于u003Cstrongu003Edubbo-2.5.3u003Cu002Fstrongu003E版本。u003Cu002Fpu003Eu003Cp class=”ql-align-justify”u003E如果你对u003Cstrongu003EStackOverflowErroru003Cu002Fstrongu003E有一定的了解,就可以知道出现这个问题的主要原因就是u003Cstrongu003E调用栈太深u003Cu002Fstrongu003E,比如常见的无限递归调用。那本文要介绍的Dubbo抛出的这个StackOverflowError又是什么原因呢?且往下看。u003Cu002Fpu003Eu003Cdiv class=”pgc-img”u003Eu003Cimg src=”http:u002Fu002Fp1.pstatp.comu002Flargeu002Fpgc-imageu002F2d7b870fcb744cfbb4e43409c70f01b3″ img_width=”1080″ img_height=”810″ alt=”惊:Dubbo居然有必现StackOverflowError的Bug” inline=”0″u003Eu003Cp class=”pgc-img-caption”u003Eu003Cu002Fpu003Eu003Cu002Fdivu003Eu003Cp class=”ql-align-justify”u003Eu003Cbru003Eu003Cu002Fpu003Eu003Culu003Eu003Cli class=”ql-align-justify”u003Eu003Cstrongu003E重现问题u003Cu002Fstrongu003Eu003Cu002Fliu003Eu003Cu002Fulu003Eu003Cp class=”ql-align-justify”u003E话不多说,直入主题。这次碰到的StackOverflowError非常好重现,只需要如下简短的代码即可。需要注意的是这里调用的是u003Cstrongu003Ecom.alibaba.dubbo.common.json.JSONu003Cu002Fstrongu003E,而不是fastjson中的u003Cstrongu003Ecom.alibaba.fastjson.JSONu003Cu002Fstrongu003E:u003Cu002Fpu003Eu003Cpre class=”ql-align-justify”u003Epackage com.afei.test.dubbo.provider.main;u003Cbru003Eimport com.alibaba.dubbo.common.json.JSON;u003Cbru003Eimport java.util.Locale;u003Cbru003Epublic class DubboTest {u003Cbru003E public static void main(String[] args) throws Exception {u003Cbru003E Locale locale = Locale.getDefault();u003Cbru003E System.out.println(JSON.json(locale));u003Cbru003E }u003Cbru003E}u003Cbru003Eu003Cu002Fpreu003Eu003Cp class=”ql-align-justify”u003E运行这段代码能得到如下异常:u003Cu002Fpu003Eu003Cpre class=”ql-align-justify”u003EException in thread “main” java.lang.StackOverflowErroru003Cbru003E at java.util.concurrent.ConcurrentHashMap.get(ConcurrentHashMap.java:936)u003Cbru003E at sun.util.locale.provider.LocaleResources.getLocaleName(LocaleResources.java:233)u003Cbru003E at java.util.Locale.getDisplayName(Locale.java:1879)u003Cbru003E at java.util.Locale.getDisplayName(Locale.java:1845)u003Cbru003E at com.alibaba.dubbo.common.bytecode.Wrapper0.getPropertyValue(Wrapper0.java)u003Cbru003E at com.alibaba.dubbo.common.json.GenericJSONConverter.writeValue(GenericJSONConverter.java:125)u003Cbru003E at com.alibaba.dubbo.common.json.GenericJSONConverter.writeValue(GenericJSONConverter.java:73)u003Cbru003E at com.alibaba.dubbo.common.json.GenericJSONConverter.writeValue(GenericJSONConverter.java:129)u003Cbru003E at com.alibaba.dubbo.common.json.GenericJSONConverter.writeValue(GenericJSONConverter.java:73)u003Cbru003E at com.alibaba.dubbo.common.json.GenericJSONConverter.writeValue(GenericJSONConverter.java:129)u003Cbru003E at com.alibaba.dubbo.common.json.GenericJSONConverter.writeValue(GenericJSONConverter.java:73)u003Cbru003E… …u003Cbru003Eu003Cu002Fpreu003Eu003Culu003Eu003Cli class=”ql-align-justify”u003Eu003Cstrongu003E分析原因u003Cu002Fstrongu003Eu003Cu002Fliu003Eu003Cu002Fulu003Eu003Cp class=”ql-align-justify”u003E由这个异常堆栈信息,我们很容易知道在u003Cstrongu003EGenericJSONConverteru003Cu002Fstrongu003E中的第73行和129行之间出现了无限递归调用,打开dubbo源码并debug,发现在调用GenericJSONConverter中的writeValue()方法时,首先会判断需要序列化的对象的类型。当对象是如下类型时会特殊处理:u003Cu002Fpu003Eu003Colu003Eu003Cli class=”ql-align-justify”u003E原生类型或者封装类型;u003Cu002Fliu003Eu003Cli class=”ql-align-justify”u003EJSONNode类型;u003Cu002Fliu003Eu003Cli class=”ql-align-justify”u003E枚举;u003Cu002Fliu003Eu003Cli class=”ql-align-justify”u003E数组;u003Cu002Fliu003Eu003Cli class=”ql-align-justify”u003EMap;u003Cu002Fliu003Eu003Cli class=”ql-align-justify”u003E集合类型;u003Cu002Fliu003Eu003Cu002Folu003Eu003Cp class=”ql-align-justify”u003E如果需要序列化的对象是其他类型,比如这里的Locale类型,序列化逻辑如下所示:u003Cu002Fpu003Eu003Cpre class=”ql-align-justify”u003Ejb.objectBegin();u003Cbru003EWrapper w = Wrapper.getWrapper(c);u003Cbru003Eu002Fu002F 得到这个对象的所有属性u003Cbru003EString pns[] = w.getPropertyNames();u003Cbru003Eu002Fu002F 遍历属性u003Cbru003Efor( String pn : pns )u003Cbru003E{u003Cbru003E u002Fu002F 被序列化的对象Locale并不是Throwable类型,忽略这段逻辑u003Cbru003E if ((obj instanceof Throwable) && (u003Cbru003E “localizedMessage”.equals(pn) u003Cbru003E || “cause”.equals(pn) u003Cbru003E || “stackTrace”.equals(pn))) {u003Cbru003E continue;u003Cbru003E }u003Cbru003E jb.objectItem(pn);u003Cbru003E u002Fu002F 得到当前遍历属性的值u003Cbru003E Object value = w.getPropertyValue(obj,pn);u003Cbru003E if( value == null || value == obj)u003Cbru003E jb.valueNull();u003Cbru003E elseu003Cbru003E u002Fu002F 无限递归死循环出现在这里u003Cbru003E writeValue(value, jb, writeClass);u003Cbru003E}u003Cbru003Eu003Cu002Fpreu003Eu003Cp class=”ql-align-justify”u003E通过这段源码的分析,我们大概可以知道Locale的属性中肯定有Locale类型的属性。由于有Locale类型的属性,导致继续调用GenericJSONConverter中的writeValue()方法,从而无限递归下去,让我们继续Debug源码验证这个猜想。u003Cu002Fpu003Eu003Cp class=”ql-align-justify”u003EDebug到String pns[] = w.getPropertyNames();,我们通过查看Locale的属性pns[]可以验证我们前面的猜想,如下图所示。Locale属性availableLocales的类型还是Locale,从而出现死循环直到抛出StackOverflowError:u003Cu002Fpu003Eu003Cdiv class=”pgc-img”u003Eu003Cimg src=”http:u002Fu002Fp3.pstatp.comu002Flargeu002Fpgc-imageu002Fe340210b074b43af973dcfc660344fd3″ img_width=”1080″ img_height=”553″ alt=”惊:Dubbo居然有必现StackOverflowError的Bug” inline=”0″u003Eu003Cp class=”pgc-img-caption”u003Eu003Cu002Fpu003Eu003Cu002Fdivu003Eu003Cpu003ELocale中有Locale类型的属性u003Cu002Fpu003Eu003Culu003Eu003Cli class=”ql-align-justify”u003Eu003Cstrongu003E解决问题u003Cu002Fstrongu003Eu003Cu002Fliu003Eu003Cu002Fulu003Eu003Cp class=”ql-align-justify”u003E那么如何解决这个问题呢?很简单,不要使用dubbo中的JSON,改为使用fastjson中的JSON,或者jackson和GSON都可以:u003Cu002Fpu003Eu003Cpre class=”ql-align-justify”u003ELocale locale = Locale.getDefault();u003Cbru003ESystem.out.println(com.alibaba.fastjson.JSON.toJSON(locale));u003Cbru003ESystem.out.println(new com.google.gson.Gson().toJson(locale)); u003Cbru003Eu003Cu002Fpreu003Eu003Cp class=”ql-align-center”u003Eu003Cbru003Eu003Cu002Fpu003Eu003Cdiv class=”pgc-img”u003Eu003Cimg src=”http:u002Fu002Fp3.pstatp.comu002Flargeu002Fpgc-imageu002F57f21bc5be474b2d8809f602a8038d24″ img_width=”299″ img_height=”277″ alt=”惊:Dubbo居然有必现StackOverflowError的Bug” inline=”0″u003Eu003Cp class=”pgc-img-caption”u003Eu003Cu002Fpu003Eu003Cu002Fdivu003Eu003Cp class=”ql-align-justify”u003Eu003Cstrongu003EDubbo Fixu003Cu002Fstrongu003Eu003Cu002Fpu003Eu003Cp class=”ql-align-justify”u003E笔者翻看dubbo issue历史,发现dubbo在2018-05-09修复了这个问题,对应的dubbo版本是2.6.3,描述为:u003Cstrongu003Eadd Locale serialize & deserialize supportu003Cu002Fstrongu003E。pull地址如下:https:u002Fu002Fgithub.comu002Fapacheu002Fdubbou002Fpullu002F1761u002Fcommits。u003Cu002Fpu003Eu003Cp class=”ql-align-justify”u003E修复的代码片段如下所示,主要改动点有:u003Cu002Fpu003Eu003Colu003Eu003Cli class=”ql-align-justify”u003E如果序列化对象是Locale类型,那么序列化方式就是调用toString()方法;u003Cu002Fliu003Eu003Cli class=”ql-align-justify”u003E如果反序列化目标对象类型是Locale,那么将value以下划线_分割,然后构造Locale对象,用法参考:JSON.parse(“zh_CN”, Locale.class);u003Cu002Fliu003Eu003Cu002Folu003Eu003Cdiv class=”pgc-img”u003Eu003Cimg src=”http:u002Fu002Fp1.pstatp.comu002Flargeu002Fpgc-imageu002F6f112d2c28674afcb380351b8be20740″ img_width=”1080″ img_height=”816″ alt=”惊:Dubbo居然有必现StackOverflowError的Bug” inline=”0″u003Eu003Cp class=”pgc-img-caption”u003Edubbo fixu003Cu002Fpu003Eu003Cu002Fdivu003Eu003Cu002Fdivu003E”

原文始发于:惊:Dubbo居然有必现StackOverflowError的Bug

主题测试文章,只做测试使用。发布者:逗乐男神i,转转请注明出处:http://www.cxybcw.com/26476.html

联系我们

13687733322

在线咨询:点击这里给我发消息

邮件:1877088071@qq.com

工作时间:周一至周五,9:30-18:30,节假日休息

QR code