今天看了下之前做的一個非同步處理任務的服務,發現佔用記憶體量比較大,達到2G,但我檢查了代碼,基本沒有static對象。但這個服務有個特點,就是每次執行一個任務的時候,會從資料庫中撈大量的資料做處理,因此我懷疑是因為有比較多的臨時對象產生,但程式沒有來的及釋放。為了驗證這個,我用了jinfo查看和臨時設定啟動參數,jmap查看記憶體佔用情況和主動觸發FGC,jstat查看gc情況,top查看整個程式佔用的記憶體量。
用jmap查看,發現記憶體佔用並沒有那麼高(free值很大),但capacity比較大。用top查看,整個程式也佔用了接近2G,那既然free那麼多,為啥程式還佔用那麼高的記憶體?是不是因為還沒有gc導致?
因此我用jmap做了一次主動gc:
/opt/jdk/bin/jmap -histo:live PID
,看了top,還是一樣大小,只是jmap出來的free值更高了(特別是老年代,新生代的used變成了0),說明gc是有起到作用的。但是,為啥top沒有變化??
後來查了jmap出來的參數,MaxHeapFreeRatio參數的意思是,當空閑記憶體數超出這個值時,會回收記憶體直到最小記憶體。而我的程式裡面,預設這個值是100,也就是說要所有記憶體都未使用才會釋放(都100%未使用了還需要釋放嗎?沒鳥用了)。為了驗證,我用jinfo臨時設定了參數:
/opt/jdk/bin/jinfo -flag MaxHeapFreeRatio=70 PID
,主動觸發gc,發現top記憶體降到了1G。問題解決了。
網上有人遇到top出來的res結果和jmap查出來的結果不一致,具體原因我也沒搞懂,但是,只要觸發了gc,而且記憶體做了回收,結果應該跟top出來的結果是一致的。
但jinfo只是臨時設定,為了程式每次啟動都生效,必須在啟動指令碼中設定。這個是在gradle中。我的服務有個build.gradle檔案,在裡面加幾個參數:
defaultJvmOpts = ["-XX:MaxHeapFreeRatio=70", "-XX:MaxTenuringThreshold=3", "-XX:MaxNewSize=1073741824"]
MaxTenuringThreshold代表對象的被回收存活次數,
MaxNewSize代表新生代記憶體大小。
執行build後自動產生啟動指令碼,搞定!
相關連結:
1、jvm啟動參數
2、jstat使用
3、各種java工具
4、列印當前java的所有參數
5、jinfo設定參數
6、別人的例子(僅做參考)