發現陷入了一個怪圈,寫文章的話,感覺只有大bug或比較值得寫的內容才會寫,每次一寫就是幾千字,爭取寫得透徹一些,但這樣,我也挺費時間,讀者也未必有這麼多時間看。
我想著,日常遇到的小bug、平時工作中的一些小的心得體會,都還是可以寫寫,這樣也才是最貼近咱們作為一線開發生活的,也不必非得是個完整且深入的主題,因此,準備搞一個專門的標籤:點滴記錄Coding之路來記錄這些。
ok,咱們開始,最近,手下開發小哥去幫忙做一個其他組的專案,但遇到一些解決不了的問題就會找我幫忙看。最近來問我了一個問題,說是他有個介面,呼叫會報記憶體溢位,在本機就能復現,不知道咋回事。
介面程式碼如下:
在一個for迴圈裡面,會去執行sql,查詢資料庫記錄,存到dataList這個列表中,然後序列化為json,這裡呢,他們使用的是fastjson。
他呼叫介面給我演示了下,上面程式碼不是個迴圈嘛,跑著跑著就報錯了,報錯的棧大概如下(這個棧來自網上,問題類似):
Exception in thread "pool-4-thread-1" java.lang.OutOfMemoryError
at com.alibaba.fastjson2.JSONWriterUTF16.writeNameRaw(JSONWriterUTF16.java:561)
at com.alibaba.fastjson2.writer.FieldWriterImpl.writeFieldName(FieldWriterImpl.java:143)
at com.alibaba.fastjson2.writer.ObjectWriter_3.write(Unknown Source)
at com.alibaba.fastjson2.writer.ObjectWriterImplList.write(ObjectWriterImplList.java:278)
at com.alibaba.fastjson2.JSON.toJSONString(JSON.java:1757)
.....
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
at java.lang.Thread.run(Thread.java:748)
剛看到這個,也沒啥思路,一開始還以為是記憶體、gc之類的問題,看了會後,決定在報錯的地方打斷點看下,到底為啥報這個。
我這邊debug了兩圈後,發現都是走到如下位置的時候報錯:
這個函數,大概就是,在初步序列化物件為字串後,要計算字串的長度,然後看看這個長度能不能寫入到底層JsonWriter的字元陣列中(會比較字串的長度和JsonWriter中陣列的長度),如果JsonWriter中陣列長度過小,這裡就要觸發擴容。
而擴容前,如果發現要擴容的大小大於maxArraySize(一個設定項),就會拋這個記憶體溢位的溢位,並不是真的發生了記憶體溢位。
當時debug的時候,看到maxArraySize大概是60w多,大概就是60多m大小。當時就很納悶,是不是查出來的資料太大了,不然即使擴容啥的,也不可能大於60M,後面果然看到資料竟然達到了幾十M大小,由於這個系統我也沒參與,這塊業務合不合理就不管了,解決問題就行。
然後我就看了下,maxArraySize賦值的地方,看看這個能不能改大點,改大了就沒事了。
protected final int maxArraySize;
protected JSONWriter(Context context, Charset charset) {
this.context = context;
this.charset = charset;
this.utf8 = charset == StandardCharsets.UTF_8;
this.utf16 = charset == StandardCharsets.UTF_16;
quote = (context.features & Feature.UseSingleQuotes.mask) == 0 ? '"' : '\'';
// 64M or 1G
maxArraySize = (context.features & LargeObject.mask) != 0 ? 1073741824 : 67108864;
}
這邊果然看到,有個註釋,64M OR 1G,果然,是個設定項,看起來,這個設定項是受LargeObject這個控制的。
一開始,我以為這個是com.alibaba.fastjson.serializer.SerializerFeature
裡的列舉項,結果並不是,沒發現是JsonWriter的設定項:
com.alibaba.fastjson2.JSONWriter.Feature
知道是設定項了,問題是怎麼設定呢?仔細看了各個方法,都不能傳這種JsonWriter的列舉啊
後邊,看了半天,發現這個方法可以傳JsonWriter的feature:
問題是,這個defaultFeatures是int,32位元整數,每個bit代表一個特性,也就是說,我得自己計算將LargeObject這個bit置為1後,整個int的值。
大家看這個feature的值:
// 十進位制為:8589934592, 二進位制為:001000000000000000000000000000000000
LargeObject(1L << 33),
我就根據這個,自己把這個bit設為1,然後算了個值出來,結果,跟我說,超過了int的範圍,導致我沒法傳參進去。
我都服了,然後開始在網上看看有沒有類似的問題,結果只找到了一篇文章。
https://blog.csdn.net/m0_68736501/article/details/132078314
解決辦法是說,升級jar包版本到2.0.16,裡面有個方法,可以傳JsonWriter的Feature列舉值進去:
JSON.toJSONString(t, JSONWriter.Feature.WriteClassName, JSONWriter.Feature.LargeObject).getBytes(DEFAULT_CHARSET);
結果我看了我們版本,都2.0.19了,版本比他還高,結果沒看到這個方法。服了,難道高版本還把這個方法刪了?
然後小夥子看我忙,就說他回去再研究研究,我說行,我也網上查下。
後邊也找到篇文章,讓他試試:https://www.exyb.cn/news/show-5352725.html,他沒說有沒有效果,但是過了一陣,他跟我說,知道問題了。
行吧,我給大家梳理下結論,我們的pom引入的依賴是:
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>2.0.19</version>
</dependency>
這個內部其實還依賴了另外的jar:
<dependency>
<groupId>com.alibaba.fastjson2</groupId>
<artifactId>fastjson2-extension</artifactId>
</dependency>
而上面的這個,又依賴了:
<dependency>
<groupId>com.alibaba.fastjson2</groupId>
<artifactId>fastjson2</artifactId>
</dependency>
差不多,就是下圖這樣的關係:
然後,導致我們專案中,其實有兩個JSON類:
com.alibaba.fastjson2.JSON; 位於fastjson2-2.0.19.jar
com.alibaba.fastjson.JSON; 位於fastjson-2.0.19.jar
而之前我們匯入的是下面那個,也就是傳統的com.alibaba.fastjson.JSON,裡面就是沒法傳JsonWriter的Feature列舉的,只有上面那個才有:
com.alibaba.fastjson2.JSON#toJSONString(java.lang.Object, com.alibaba.fastjson2.JSONWriter.Feature...)
/**
* Serialize Java Object to JSON {@link String} with specified {@link JSONReader.Feature}s enabled
*
* @param object Java Object to be serialized into JSON {@link String}
* @param features features to be enabled in serialization
*/
static String toJSONString(Object object, JSONWriter.Feature... features) {
所以,剩下的事情,簡單了,修改import的類為com.alibaba.fastjson2.JSON即可,然後序列化時傳入feature:
String previewDataJson = JSON.toJSONString(dataList,LargeObject);
問題解決。
新專案建議還是用jackson算了,當然了,這個專案也不是我主導,而且都開發快完成了,就這樣吧,一般大問題也沒有,有就再改吧。