實戰項目中Java heap space錯誤的解決

部標GPS通信系統在上線以後,通過不斷調試,終於穩定運行一段時間,後來又遇到了Java heap space錯誤異常!日誌以下:html

 

說明系統中有未釋放的對象。如何找出這些未釋放對象以及監控JVM堆內存,優化代碼釋放內存對象呢?還有JVM的垃圾回收機制是如何運做的呢?
java

首先在系統啓動運行的時候打開記錄GC詳細信息,運行腳本以下:程序員

 看看GC詳細日誌,當GC到13400屢次的時候新生代和老生代的堆內存幾乎用滿了,頻繁觸發Full GC (Ergonomics) ,直到沒有內存空間給新生對象了。因此JVM拋出了內存溢出錯誤!進而致使程序崩潰。算法

 

首先簡單瞭解下JVM垃圾回收機制:數據庫

從上圖能夠看出 GC 的主要收集區域,包括 PSYoungGen(年輕代)、ParOldGen(老年代)、Metaspace(元數據區)。框架

新生代:新建立的對象都是用新生代分配內存,Eden空間不足時,觸發Minor GC,這時會把存活的對象轉移進幸存者Survivor區。
老年代:老年代用於存放通過屢次Minor GC以後依然存活的對象。jvm

新生代的GC(Minor GC):新生代一般存活時間較短基於Copying算法進行回收,所謂Copying算法就是掃描出存活的對象,並複製到一塊新的徹底未使用的空間中,對應於新生代,就是在Eden和FromSpace或ToSpace之間copy。工具

新生代採用空閒指針的方式來控制GC觸發,指針保持最後一個分配的對象在新生代區間的位置,當有新的對象要分配內存時,用於檢查空間是否足夠,不夠就觸發GC。當連續分配對象時,對象會逐漸從Eden到Survivor,最後到老年代。優化

老年代的GC(Major GC/Full GC):老年代與新生代不一樣,老年代對象存活的時間比較長、比較穩定,所以採用標記(Mark)算法來進行回收,所謂標記就是掃描出存活的對象,而後再進行回收未被標記的對象,spa

回收後對用空出的空間要麼進行合併、要麼標記出來便於下次進行分配,總之目的就是要減小內存碎片帶來的效率損耗。

附上堆結構圖:

 

至此,在已經嘗試了將Java虛擬機中Xms(初始堆大小-2G)和Xmx(最大堆大小-4G)參數調大以後也無果,只能從代碼入手, 對象一直堆積於老年代未被釋放,應該是代碼中對象未被釋放。

爲了找出代碼中未被釋放的對象,這裏運用到了內存監控工具jconsole和內存堆對象統計工具jmap

jconsole.exe位於bin目錄下,用法也挺簡單的,用於調優後的監控還不錯,這裏留個圖不作介紹了

 

這裏運用jmap(jdk自帶的jvm內存分析的工具),將程序堆對象統計出來的結果以下:

發現到代碼問題:應該是解析協議的時候CanDataItem對象不斷被new出來,使用完了之後並未被GC回收留在老生代,

 找到CanDataItem使用完成的地方作了以下修改:

修改代碼再將T808Message等類用完了的地方作了釋放,以後終於解決了問題!修改代碼之後再次使用jmap統計以下,正常了!

附 - jmap輸出中class name非自定義類的說明:

BaseType Character Type Interpretation
B byte signed byte
C char Unicode character
D double double-precision floating-point value
F float single-precision floating-point value
I int integer
J long long integer
L; reference an instance of class
S short signed short
Z boolean true or false
[ reference one array dimension,[I表示int[]

 

總結致使java.lang.OutOfMemoryError異常的常見緣由有如下幾種:

  • 內存中加載的數據量過於龐大,如一次從數據庫取出過多數據;
  • 集合類中有對對象的引用,使用完後未清空,使得JVM不能回收;
  • 代碼中存在死循環或循環產生過多重複的對象實體-----(本例中);
  • 使用的第三方軟件中的BUG;
  • 啓動參數內存值設定的太小;

至此問題總算解決了,一般狀況下,Java開發人員並不須要去關心JVM是如何運行的。即便不理解JVM的工做原理,也不會給開發人員帶來過多困惑。Java程序員更容易忽視基礎技術。

「蚓無爪牙之利,筋骨之強,上食埃土,下飲黃泉,用心一也。蟹六跪而二螯,非蛇蟮之穴無可寄託者,用心躁也」。對於技術人員來講,若是長期忽略自身技術的根基而去一昧地追求高層框架技術,這無疑是捨本求末的作法。

JVM的出現,爲程序員屏蔽了操做系統與硬件的細節,使得程序員從諸如內存管理這樣的繁瑣任務中解放出來。但這不併等同於容許Java程序員放棄對基礎的重視。

事實上,任何平臺的程序員都應當瞭解平臺的基本特性、實現機制以及接口,這是提升自身修養的必經之路。對於Java程序員來講,咱們仍是須要多多瞭解JVM。

瞭解JVM的基本實現機制,不只對於解決實際應用中諸如GC等虛擬機問題時有直接幫助,還有利於咱們更好地理解語言自己。

 

轉載請註明出處!謝謝!https://www.cnblogs.com/lys_013/p/9605352.html

相關文章
相關標籤/搜索