一次關於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程式碼- privatestaticfinal Gson gson = new Gson();//做成單例
- ......
- out.add(gson.fromJson(new String(beanBytes), Trade.class));
第四部:壓測驗證結果
從圖片顯而易見,沒有記憶體洩露,最後那一條下墜的橫線,是我為了確認問題,做了一次全回收,回收後,記憶體釋放到只有20多M,非常好。