一次關於Netty+Gson造成內存泄露的分析排查

最近做了一個內部系統之間的數據同步服務器,client端通過socket發送經過壓縮的json數據到server端,server完成數據解碼和保存。server架構:netty+Gson解碼
 

在做壓力測試的時候,竟然發現server內存泄露。分析內存泄露的時候,其實我們可以從簡單方法入手,因爲jdk1.6後自身就帶有不錯的內存分析工具,而且我認爲使用好這些工具基本足夠高,因爲如果你對這些工具的理解不深入,即使再強大的工具也是對你幫助不大。

 

開始分析:

 

第一步:jmap -heap <pid> 看個內存快照

 

 using thread-local object allocation.
Parallel GC with 4 thread(s)

Heap Configuration:
   MinHeapFreeRatio = 40
   MaxHeapFreeRatio = 70
   MaxHeapSize      = 536870912 (512.0MB)
   NewSize          = 1310720 (1.25MB)
   MaxNewSize       = 17592186044415 MB
   OldSize          = 5439488 (5.1875MB)
   NewRatio         = 2
   SurvivorRatio    = 8
   PermSize         = 21757952 (20.75MB)
   MaxPermSize      = 67108864 (64.0MB)
   G1HeapRegionSize = 0 (0.0MB)

Heap Usage:
PS Young Generation
Eden Space:
   capacity = 160563200 (153.125MB)
   used     = 131079984 (125.00761413574219MB)
   free     = 29483216 (28.117385864257812MB)
   81.63762555803571% used
From Space:
   capacity = 9175040 (8.75MB)
   used     = 0 (0.0MB)
   free     = 9175040 (8.75MB)
   0.0% used
To Space:
   capacity = 9043968 (8.625MB)
   used     = 0 (0.0MB)
   free     = 9043968 (8.625MB)
   0.0% used
PS Old Generation
   capacity = 357957632 (341.375MB)
   used     = 357954568 (341.37207794189453MB)
   free     = 3064 (0.00292205810546875MB)
   99.9991440327776% used
PS Perm Generation
   capacity = 25690112 (24.5MB)
   used     = 25614800 (24.428176879882812MB)
   free     = 75312 (0.0718231201171875MB)
   99.70684440768495% used

10160 interned Strings occupying 880136 bytes.

經過一段時間運行,內存老生代滿了,而且一直釋放不了。
結合jcosonle運行圖會更直觀

 

   

這時候,再結合命令:jstat -gcutil <pid>  10000 20查看內存使用和回收快照,發現幾十次大量的輕回收和全回收,輕回收每次時間達到2秒。

 

 

 第二部:既然確定存在內存泄露,那麼就定位誰泄露,這一般是最難的地方。使用命令:jmap -histo <pid>

 


 num     #instances         #bytes  class name
----------------------------------------------
   1:       1186682       85441104  java.lang.reflect.Field
   2:       1186506       56952288  com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$1
   3:       1271453       50858120  java.util.LinkedHashMap$Entry
   4:        733627       38708968  [C
   5:       1268334       30440016  com.google.gson.reflect.TypeToken
   6:        437746       29974120  [I
   7:        215160       22299464  [Ljava.util.HashMap$Entry;
   8:        122745       17675280  java.text.DecimalFormat
   9:        191610       14843680  [Ljava.lang.Object;
  10:        122746       13747552  java.util.GregorianCalendar
  11:        122746       11783616  sun.util.calendar.Gregorian$Date
  12:        116114       10890136  [B
  13:        336981       10783392  java.util.HashMap$Entry


 。。。。。


Total      11229733      542330424

就憑前兩句,我大概斷定是gson的問題。產生了大量gson的類。

   1:       1186682       85441104  java.lang.reflect.Field
   2:       1186506       56952288  com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$1

但是,我是不會懷疑是gson的問題,而是在思考是不是使用方式不正確。

 

 

第三部:代碼分析

 

gson是放在decoder裏面對報文進行解碼的

 

Java代碼  

out.add(new Gson().fromJson(new String(beanBytes), Trade.class));  

 

這裏省略1萬字各種測試對比,反正最後結果改成這樣:

 

Java代碼  

private static final Gson gson = new Gson();//做成單例  
......  
out.add(gson.fromJson(new String(beanBytes), Trade.class));  

 

第四部:壓測驗證結果

 

 

從圖片顯而易見,沒有內存泄露,最後那一條下墜的橫線,是我爲了確認問題,做了一次全回收,回收後,內存釋放到只有20多M,非常好。
 

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