dubbo泛化調用內存溢出報OutOfMemoryError異常

dubbo客戶端泛化調用服務端接口時,客戶端程序分配2G內存,但是服務最多運行1小時就會內存溢出,

異常信息如下(異常顯示SendThread這個線程出出現了內存溢出):

[rpc_dubbo_call_thread_1_1-SendThread(127.0.0.1:2182)] WARN  org.apache.zookeeper.server.ZooKeeperThread - Exception occurred from thread rpc_dubbo_call_thread_1_1-SendThread(127.0.0.1:2182)
java.lang.OutOfMemoryError: Java heap space
	at java.util.HashMap.newNode(HashMap.java:1750)
	at java.util.HashMap.putVal(HashMap.java:631)
	at java.util.HashMap.put(HashMap.java:612)
	at java.util.HashSet.add(HashSet.java:220)
	at java.util.AbstractCollection.addAll(AbstractCollection.java:344)
	at org.apache.zookeeper.ZooKeeper$ZKWatchManager.materialize(ZooKeeper.java:177)
	at org.apache.zookeeper.ClientCnxn$EventThread.queueEvent(ClientCnxn.java:477)
	at org.apache.zookeeper.ClientCnxn$SendThread.run(ClientCnxn.java:1176)

爲便於快速重現該異常,我將程序得運行內存改爲512M,幾分鐘之後老年代迅速佔用100%,且FULL GC 無用!
分析服務器堆棧:
在這裏插入圖片描述
通過堆棧dump,其分析結果與異常信息一致,爲sendThread出出現內存溢出,通過下圖可見,主要是org.apache.zookeeper.ZooKeeper$ZKWatchManager相關得實例大量再堆裏面創建並最終導致內存溢出
在這裏插入圖片描述

在這裏插入圖片描述
在這裏插入圖片描述
到此,可以斷定是dubbo異常,由於程序只有一個地方用到了dubbo,且使用得是dubbo得泛化調用模式,由此我基本可以斷定問題所在即在此處:

        ReferenceConfig<GenericService> reference = new ReferenceConfig<GenericService>();
        reference.setApplication(application); 
        reference.setRegistry(registry); 
        reference.setInterface(interfaceClass); // 接口名 
        reference.setGeneric(true); // 聲明爲泛化接口 
        
        //ReferenceConfig實例很重,封裝了與註冊中心的連接以及與提供者的連接,
        //需要緩存,否則重複生成ReferenceConfig可能造成性能問題並且會有內存和連接泄漏。
        //API方式編程時,容易忽略此問題。
        //這裏使用dubbo內置的簡單緩存工具類進行緩存
        
        ReferenceConfigCache cache = ReferenceConfigCache.getCache();
        GenericService genericService = cache.get(reference); 
        if(genericService == null) {
        	cache.destroy(reference);
        	throw new IllegalStateException("服務不可用");
        }
        // 用com.alibaba.dubbo.rpc.service.GenericService可以替代所有接口引用 
        return genericService.$invoke(methodName, new String[] {"java.util.Map"}, parameters);
    }

就如上面代碼中得註釋所指,由於ReferenceConfig比較重,因此必須要緩存起來,由於程序在泛化調用時,存在多個不同接口,因此肯定會有多個ReferenceConfig生成,如果不緩存得話,隨着調用得次數增多,肯定會導致
ReferenceConfig實例越來越多,佔用許多系統資源,但是我已經緩存了的,爲什麼還會出現這個問題呢,我搜了一下dubbo下關於內存溢出得issue,基本上看了一遍,發現基本上都懷疑是ReferenceConfig出了問題,

於是,我將ReferenceConfig變成單例,再次啓動程序,發現程序得內存使用率大爲改善,此時更加斷定就是泛化調用多個不同接口導致ReferenceConfig出現問題

查看dubbo源碼中consumer得demo,發現在泛化調用時引入了一個新的對象DubboBootstrap,按照最新示例將原有泛化調用方式改正之後如下:

    public Object genericInvoke(String interfaceClass, String methodName, Object[] parameters){
    	System.out.println("當前調用得接口名和方法爲:"+interfaceClass+";"+methodName);
        ReferenceConfig<GenericService> reference = new ReferenceConfig<GenericService>();
        reference.setInterface(interfaceClass); // 接口名 
        reference.setGeneric("true"); // 聲明爲泛化接口 
        
        
        DubboBootstrap bootstrap = DubboBootstrap.getInstance();
        bootstrap.application(application)
                .registry(registry)
                .reference(reference)
                .start();

        
        ReferenceConfigCache cache = ReferenceConfigCache.getCache();
        GenericService genericService = cache.get(reference); 
        if(genericService == null) {
        	cache.destroy(reference);
        	throw new IllegalStateException("服務不可用");
        }
        return genericService.$invoke(methodName, new String[] {"java.util.Map"}, parameters);
    }

分析程序運行狀況發現,堆內存得佔用恢復正常;
在這裏插入圖片描述

這次遇到得問題應該時dubbo之前版本遺留得一個bug,程序中泛化調用時會用到比較多的接口,導致正常情況下ReferenceConfig實例數量本身就多,可能觸發了dubbo隱藏得某些bug,目前具體原因還未知,不過在新版本dubbo中已經將setGeneric和setApplication方法廢棄掉了,由於我一直沒有十分關注dubbo社區,具體原因我也不清楚,不過好在問題解決了…
在這裏插入圖片描述
在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章