JSON字符串反序列化 動(dòng)態(tài)泛型
需求:定時(shí)任務(wù)掃描,反射調(diào)用目標(biāo)對(duì)象,但是,方法的傳參不是固定的。
方案一:將方法參數(shù)存成JSON字符串,然后JSON反序列化成對(duì)象,然后反射調(diào)用
目標(biāo)方法時(shí)這樣的:
CommandResp sendXXX(BaseCommandApiDTO<XXX> baseCommandApiDTO);
方式一:FastJson
Class mainBody = Class.forName(entity.getMainBodyType());
ParameterizedTypeImpl parameterizedType = new ParameterizedTypeImpl(new Type[]{mainBody}, null, BaseCommandApiDTO.class);
Object obj = JSON.parseObject(entity.getMsgText(), parameterizedType);
CommandResp resp = ReflectUtil.invoke(serviceObj, methodName, obj);
方式二:Jackson
public class ObjectMapperHolder {
private static final ObjectMapper objectMapper = new ObjectMapper();
public static ObjectMapper getObjectMapper() {
objectMapper.registerModule(new Jdk8Module());
objectMapper.registerModule(new JavaTimeModule());
return objectMapper;
}
}
ObjectMapper mapper = ObjectMapperHolder.getObjectMapper();
JavaType javaType = mapper.getTypeFactory().constructParametricType(BaseCommandApiDTO.class, mainBody);
Object obj = mapper.readValue(entity.getMsgText(), javaType);
CommandResp resp = ReflectUtil.invoke(serviceObj, methodName, obj);
實(shí)踐中發(fā)現(xiàn),這兩種方式容易導(dǎo)致OOM
方案二:直接將參數(shù)對(duì)象存到數(shù)據(jù)庫(kù)中
數(shù)據(jù)庫(kù)對(duì)應(yīng)字段設(shè)置BLOB類型(這里設(shè)置的是MEDIUMBLOB) ,對(duì)應(yīng)的java字段類型是byte[]
直接將Java對(duì)象序列化為二進(jìn)制數(shù)據(jù),這種方式的存儲(chǔ)和讀取速度非常快,因?yàn)樾蛄谢头葱蛄谢^(guò)程不需要轉(zhuǎn)換格式。
// 寫(xiě)入對(duì)象
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos);
oos.writeObject(baseCommandApiDTO);
oos.flush();
byte[] data = bos.toByteArray();
// 讀取對(duì)象
ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(entity.getMsgObj()));
Object obj = ois.readObject();
最后的最后,優(yōu)化建議:
1、盡量不要在數(shù)據(jù)庫(kù)中存json字符串,如果非要存,建議字段類型設(shè)置為json,這樣可以節(jié)省空間。因?yàn)槟銦o(wú)法控制json字符串的長(zhǎng)度,所以長(zhǎng)度設(shè)置是個(gè)問(wèn)題,另外json反序列化比較占內(nèi)存,想想從一個(gè)對(duì)象變成兩個(gè)對(duì)象當(dāng)然要占用更多內(nèi)存。
2、長(zhǎng)度很大的字段(比如blob類型的)建議單獨(dú)存一張關(guān)聯(lián)表
補(bǔ)充知識(shí):
1、一個(gè)對(duì)象序列化成JSON字符串后,這個(gè)字符串會(huì)比原對(duì)象占用更多的內(nèi)存。?
在序列化過(guò)程中,對(duì)象會(huì)被轉(zhuǎn)換為字符串形式存儲(chǔ)在堆內(nèi)存中。由于字符串的存儲(chǔ)方式與對(duì)象不同,通常會(huì)導(dǎo)致內(nèi)存占用增加。具體來(lái)說(shuō),未序列化時(shí),對(duì)象存儲(chǔ)在堆內(nèi)存中,而序列化后,這些對(duì)象被轉(zhuǎn)換為字符串,字符串也需要額外的存儲(chǔ)空間。此外,序列化過(guò)程中可能會(huì)涉及到更多的計(jì)算和內(nèi)存分配,進(jìn)一步增加了資源消耗?。
序列化對(duì)內(nèi)存占用的具體影響:
- 存儲(chǔ)空間增加?:序列化后的字符串通常會(huì)比原對(duì)象占用更多的存儲(chǔ)空間,因?yàn)樽址枰嗟淖止?jié)來(lái)存儲(chǔ)。例如,JSON格式的序列化會(huì)包含很多結(jié)構(gòu)化的符號(hào)和字符,這些都會(huì)增加存儲(chǔ)需求?。
- ?計(jì)算資源消耗?:序列化過(guò)程需要計(jì)算對(duì)象的屬性和值,并將其轉(zhuǎn)換為字符串形式。這個(gè)過(guò)程可能會(huì)消耗更多的CPU資源,尤其是在處理復(fù)雜對(duì)象時(shí)?。
- ?內(nèi)存占用變化?:序列化后,對(duì)象被存儲(chǔ)為字符串,如果對(duì)象較大或數(shù)量較多,內(nèi)存占用可能會(huì)顯著增加。例如,一個(gè)包含大量數(shù)據(jù)的集合在序列化后,其占用的內(nèi)存可能是未序列化時(shí)的兩倍或更多。
2、日志輸出不當(dāng)也有可能造成內(nèi)存溢出
比如:log.info("請(qǐng)求參數(shù): {}", JSON.toJSONString(DTO))
即使日志級(jí)別是ERROR,但是當(dāng)代碼執(zhí)行到這一行時(shí),仍然會(huì)執(zhí)行JSON.toJSONString(DTO)這句,而將對(duì)象序列化成json字符串的這個(gè)動(dòng)作會(huì)消耗一定的內(nèi)存,尤其是當(dāng)請(qǐng)求量大,而且打印的對(duì)象也很多的時(shí)候就有可能成為壓垮JVM的一根稻草。
在這里log.info()起到的作用是決定要不要把日志輸出到控制臺(tái)或文件。
3、對(duì)于大數(shù)據(jù)量,可以使用流式解析,逐行讀取和處理JSON數(shù)據(jù),避免一次性將整個(gè)JSON加載到內(nèi)存中。
參考博客 《對(duì)象序列化內(nèi)存占用問(wèn)題》
http://www.rzrgm.cn/Nuwa/p/18027475

浙公網(wǎng)安備 33010602011771號(hào)