部標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異常的常見緣由有如下幾種:
至此問題總算解決了,一般狀況下,Java開發人員並不須要去關心JVM是如何運行的。即便不理解JVM的工做原理,也不會給開發人員帶來過多困惑。Java程序員更容易忽視基礎技術。
「蚓無爪牙之利,筋骨之強,上食埃土,下飲黃泉,用心一也。蟹六跪而二螯,非蛇蟮之穴無可寄託者,用心躁也」。對於技術人員來講,若是長期忽略自身技術的根基而去一昧地追求高層框架技術,這無疑是捨本求末的作法。
JVM的出現,爲程序員屏蔽了操做系統與硬件的細節,使得程序員從諸如內存管理這樣的繁瑣任務中解放出來。但這不併等同於容許Java程序員放棄對基礎的重視。
事實上,任何平臺的程序員都應當瞭解平臺的基本特性、實現機制以及接口,這是提升自身修養的必經之路。對於Java程序員來講,咱們仍是須要多多瞭解JVM。
瞭解JVM的基本實現機制,不只對於解決實際應用中諸如GC等虛擬機問題時有直接幫助,還有利於咱們更好地理解語言自己。
轉載請註明出處!謝謝!https://www.cnblogs.com/lys_013/p/9605352.html