一次線上JVM內存異常排查 -- 對象之間的複製

一、現象

2月11日網關在短期內出現20+的訪問出錯,查看kibina以下:java

image-20190214103709965

根據trace獲得具體的堆棧異常,發現都是負載均衡同一個pod均顯示鏈接異常:api

image-20190214103941508

去grafana查看該pod運行狀況,能夠發現內存忽然降低的狀況,同時該pod已經達到內存上限(8G),當資源申請不到的狀況下,該pod可能存在重啓的狀況:緩存

image-20190214104227893

查看ad服務的日誌驗證了當時確實發生了重啓:app

image-20190214104504286

此時能夠基本定位到由於內存問題使得服務重啓,從而網關沒法負載均衡到該服務實例致使網關報錯,接下來須要明確是什麼緣由讓該pod的內存佔用如此之高。負載均衡

2、內存問題定位

  • 根據grafana的圖表,能夠看出堆內存比較正常,而非堆內存出現了異常(使用率超過100%):

image-20190214110855773

​ 非堆就是JVM留給本身用的,方法區、JVM內部處理或優化所需的內存(如JIT編譯後的代碼緩存)、每一個類結構(如運行時常數池、字段和方法數據)以及方法和構造方法 的代碼都在非堆內存中。優化

  • 用JProfiler打開該文件(也能夠用jVisualVM),找到Biggest Objects,而後發如今存活對象中存在大量的AdDto這個類的實例,大量這些對象整整佔用了1G多:spa

    image-20190214113256395

    image-20190214153409092

    3、代碼檢查

回到代碼中,檢查AdDto的生成方式,發現用了以下的操做:3d

image-20190214113956636

modelMapper每一次調用addMapping都將建立一份該類的結構(經過字節碼而後由類加載器加載),查閱官網相關文檔。若是轉換類型肯定,應該將ModelMapper設置成單例(modelmapper.org/user-manual…日誌

image-20190214141441952

4、線下重現

在普通接口中用這段代碼驗證猜測,能夠很明顯看到非堆內存一路猛漲,而且加載類的數量也在一路上升,基本證明了問題:code

image-20190214145841931

image-20190214145826877

image-20190214145906502

5、修復並驗證

將代碼改爲以下形式並從新壓測:

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);
    }
複製代碼

發現非堆內存穩定,類加載數量穩定:

image-20190214150539240

image-20190214150752900
相關文章
相關標籤/搜索