buguge - Keep it simple,stupid

知识就是力量,但更重要的,是运用知识的能力why buguge?

导航

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

在此做记录。

 

posted on 2018-12-18 21:17  buguge  阅读(10758)  评论(2编辑  收藏  举报