1、現(xiàn)象
2月11日網(wǎng)關(guān)在短時間內(nèi)出現(xiàn)20+的訪問出錯,查看kibina如下:
根據(jù)trace得到具體的堆棧異常菠红,發(fā)現(xiàn)都是負(fù)載均衡同一個pod均顯示連接異常:
去grafana查看該pod運行情況,可以發(fā)現(xiàn)內(nèi)存突然下降的情況,同時該pod已經(jīng)達(dá)到內(nèi)存上限(8G)刷后,當(dāng)資源申請不到的情況下,該pod可能存在重啟的情況:
查看ad服務(wù)的日志驗證了當(dāng)時確實發(fā)生了重啟:
此時可以基本定位到因為內(nèi)存問題使得服務(wù)重啟渊抄,從而網(wǎng)關(guān)無法負(fù)載均衡到該服務(wù)實例導(dǎo)致網(wǎng)關(guān)報錯尝胆,接下來需要明確是什么原因讓該pod的內(nèi)存占用如此之高。
二护桦、內(nèi)存問題定位
- 根據(jù)grafana的圖表含衔,可以看出堆內(nèi)存比較正常,而非堆內(nèi)存出現(xiàn)了異常(使用率超過100%):
? 非堆就是JVM留給自己用的嘶炭,方法區(qū)抱慌、JVM內(nèi)部處理或優(yōu)化所需的內(nèi)存(如JIT編譯后的代碼緩存)逊桦、每個類結(jié)構(gòu)(如運行時常數(shù)池眨猎、字段和方法數(shù)據(jù))以及方法和構(gòu)造方法 的代碼都在非堆內(nèi)存中。
-
用JProfiler打開該文件(也可以用jVisualVM)强经,找到Biggest Objects睡陪,然后發(fā)現(xiàn)在存活對象中存在大量的AdDto這個類的實例,大量這些對象整整占用了1G多:
image-20190214113256395image-20190214153409092三、代碼檢查
回到代碼中兰迫,檢查AdDto的生成方式信殊,發(fā)現(xiàn)用了如下的操作:
modelMapper每一次調(diào)用addMapping都將創(chuàng)建一份該類的結(jié)構(gòu)(通過字節(jié)碼然后由類加載器加載),查閱官網(wǎng)相關(guān)文檔汁果。如果轉(zhuǎn)換類型確定涡拘,應(yīng)該將ModelMapper設(shè)置成單例(http://modelmapper.org/user-manual/faq/):
四、線下重現(xiàn)
在普通接口中用這段代碼驗證猜想据德,可以很明顯看到非堆內(nèi)存一路猛漲鳄乏,并且加載類的數(shù)量也在一路上升,基本證實了問題:
五棘利、修復(fù)并驗證
將代碼改成如下形式并重新壓測:
private static ModelMapper modelMapper;
static {
modelMapper = new ModelMapper();
TypeMap<Person, PersonDTO> typeMap = modelMapper.createTypeMap(Person.class, PersonDTO.class);
typeMap.addMappings(mapper -> {
mapper.skip(PersonDTO::setAge);
});
}
@GetMapping("api/benchmark/qa")
public PersonDTO qa() {
Person p = new Person();
p.setName("wangao");
p.setAge(30);
return modelMapper.map(p, PersonDTO.class);
}
發(fā)現(xiàn)非堆內(nèi)存穩(wěn)定橱野,類加載數(shù)量穩(wěn)定: