今天在一个web项目里开发功能,记录日志用到了fastjson的序列化,把类型为RetreatRecord的数据对象序列化后打印出来。结果出现StackOverflowError。先贴出来异常堆栈:java
Exception in thread "main" java.lang.StackOverflowError at com.alibaba.fastjson.serializer.JSONSerializer.getContext(JSONSerializer.java:109) at com.alibaba.fastjson.serializer.JavaBeanSerializer.writeReference(JavaBeanSerializer.java:251) at Serializer_1.write1(Unknown Source) at Serializer_1.write(Unknown Source) at com.alibaba.fastjson.serializer.JSONSerializer.writeWithFieldName(JSONSerializer.java:390) //下面3行堆栈重复300屡次 at Serializer_1.write1(Unknown Source) at Serializer_1.write(Unknown Source) at com.alibaba.fastjson.serializer.JSONSerializer.writeWithFieldName(JSONSerializer.java:390)
经排查缘由,发现派生类RetreatRecord继承自DataEntity,DataEntity里有一个User currentUser字段。User也派生自DataEntity。currentUser的get方法以下:web
public User getCurrentUser() { if(null==currentUser){ currentUser=new User(); } return currentUser; }
问题就出如今了currentUser为null时给其初始化的这句上。json
debug程序可见,fastjson包里JSONSerializer.java的以下方法被死循环执行,直到堆栈溢出。this
// D:\workspace\m3\com\alibaba\fastjson\1.2.6\fastjson-1.2.6-sources.jar!\com\alibaba\fastjson\serializer\JSONSerializer.java public final void writeWithFieldName(Object object, Object fieldName, Type fieldType, int fieldFeatures) { try { if (object == null) { out.writeNull(); return; } Class<?> clazz = object.getClass(); ObjectSerializer writer = getObjectWriter(clazz); writer.write(this, object, fieldName, fieldType, fieldFeatures); } catch (IOException e) { throw new JSONException(e.getMessage(), e); } }
分析:咱们知道fastjson是基于流写入的。不难看出,在调用getCurrentUser时,由于currentUser是null,因此要给currentUser初始化,这时fastjson又要调用其getCurrentUser方法,而后又由于currentUser是null而不得再也不给currentUser初始化,如此反复。。。,必然致使StackOverflow。spa
简化我遇到的状况,你们能够运行下面的代码来复现这个bug:debug
package fastjsonstackoverflow; import java.io.Serializable; public class MyEntity implements Serializable { String id; MyEntity currentUser; public String getId() { return id; } public void setId(String id) { this.id = id; } /** * 即便没有定义length字段,fastjson序列化不会出现异常 * @return */ public int getLength(){ return 0; } public MyEntity getCurrentUser() { if(null==currentUser){ currentUser=new MyEntity(); } return currentUser; } public void setCurrentUser(MyEntity currentUser) { this.currentUser = currentUser; } } package fastjsonstackoverflow; import com.alibaba.fastjson.JSONObject; public class MainTest { public static void main(String[] args) { MyEntity entity = new MyEntity(); // System.out.println("mydata:"+entity.getCurrentUser()); System.out.println("mydata:" + JSONObject.toJSONString(entity)); } }
ps:今天经过查看fastjson源码,了解到java中的移位运算符>> <<,日志
<< : 左移运算符,num << 1,至关于num乘以2code
>> : 右移运算符,num >> 1,至关于num除以2对象
在此作记录。blog