fastjson序列化出現StackOverflowError


今天在一個web項目里開發功能,記錄日志用到了fastjson的序列化,把類型為RetreatRecord的數據對象序列化后打印出來。結果出現StackOverflowError。先貼出來異常堆棧:

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方法如下:

    public User getCurrentUser() {
        if(null==currentUser){
            currentUser=new User();
        }
        return currentUser;
    }

 

 

問題就出現在了currentUser為null時給其初始化的這句上。

debug程序可見,fastjson包里JSONSerializer.java的如下方法被死循環執行,直到堆棧溢出。

// 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。

 

簡化我遇到的情況,大家可以運行下面的代碼來復現這個bug:

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乘以2

>>      :     右移運算符,num >> 1,相當於num除以2

在此做記錄。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM