今天在一個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