天天看點

Heap Dump分析 java.lang.OutOfMemoryError: Java Heap space

背景:tomcat伺服器平均一天一次昏迷狀态,等待半天無響應,像是死了而端口還能通,但是一直不響應,後來就挂了,在日志中發現了java.lang.OutOfMemoryError: Java heap space,想到是否記憶體有問題

首先是了解了一下堆記憶體

-Xms5000M
-Xmx5000M
-XX:NewRatio=3
-XX:PermSize=200M
-XX:MaxPermSize=512M
           

網上查了很多資料,建議-Xmx設定為系統實體記憶體的1/4,-Xms為1/64,堆記憶體根據需要在這兩個參數之間變化,此處設定為一樣的,避免了來回切換,伺服器實體記憶體32G,這個設定也算合理,就算是增加堆記憶體,也是有限,解決不了根本問題,是以想到使用監控工具看下記憶體情況

開啟遠端監控,當應用服務加入到系統任務之後就需要開啟遠端監控才能通路

輕按兩下tomcat/bin/tomcatw.exe,點選java标簽,在options文本框末尾添加

-Djava.rmi.server.hostname=172.16.100.171
-Dcom.sun.management.jmxremote.port=8090
-Dcom.sun.management.jmxremote.ssl=false
-Dcom.sun.management.jmxremote.authenticate=false
           
Heap Dump分析 java.lang.OutOfMemoryError: Java Heap space

這個地方還可以修改-Xmx等參數,具體可以自己搜尋,下面三個文本框内容要清空

找到jdk根目錄下bin/jvisualvm.exe,輕按兩下打開

Heap Dump分析 java.lang.OutOfMemoryError: Java Heap space

重點關注監視标簽頁的堆标簽頁,幾分鐘的時間堆記憶體一下從2G升到4G,比較幸運的是,這個剛好是我監控開始幾分鐘後出現的剛好捕捉到,然後前線立馬就有電話打進來了,情況跟之前類似,沒有等到記憶體溢出就重新開機了服務,是以快照反應的情況是基本符合,這次分析以這個時間點為樣本

在堆标簽的右上角有個堆dump按鈕,點選之後,jvisualvm會自動生成heapdump快照

分析heapdump快照檔案有很多種工具,jvisualvm自身也可以分析,載入heapdump檔案,檔案較大時需要等一會,打開後,找到類标簽頁,清單中預設按照類的執行個體大小排列

Heap Dump分析 java.lang.OutOfMemoryError: Java Heap space

 比如此次占用最多的類型就是java.lang.String,輕按兩下這個類型可以看到對應執行個體

Heap Dump分析 java.lang.OutOfMemoryError: Java Heap space

但是這個執行個體太普遍了,且不是全局變量,暫時沒啥好辦法往下分析(因為第一次用,網上也沒找到教程怎麼分析,看到這麼多陌生的參數之後有點懵,其實從類型這裡可以看到是查詢資料庫的結果RowHandlerCallback放在了ArrayList裡面,然後又放在Object[]數組中,但這些都是後來才明白的),是以此處介紹IBM HeapAnalyzer

下載下傳最新版本,解壓後就是一個jar檔案,啟動指令,426是版本号,不是目前最新版本

C:\Users\Administrator>java -Xmx12g -jar C:\Users\Administrator\Desktop\ha426.jar

heapdump檔案越大,-Xmx設定越大,此次我的快照檔案為5.7G,預設啟動參數為12g,剛開始10g也成功過,後來不行了又擴大到12G,解析後的頁面大概長這個樣子

Heap Dump分析 java.lang.OutOfMemoryError: Java Heap space

右鍵根節點,有個locate a leak suspect菜單,點選後可以自動找到疑似記憶體溢出對象,這裡就是選中節點的第一個子節點

Heap Dump分析 java.lang.OutOfMemoryError: Java Heap space

就是這個類型,這裡可以看出,是一個ArrayList<Form>的集合占用較大,源頭是從資料庫查詢解析拿到的,一開始隻注意到第一個大節點,後來發現第二個第三個大節點都是這個類型,加起來也就是占用89%記憶體

下面是根據業務分析的結果

看了一下這個類型對應的表字段,有一個nclob類型,且查詢語句好多都是select * from form,這樣查詢的結果就是不管nclob字段是否需要都會查詢出來,想象一下有N多對象的情況

根源是找到了,但是這個表的業務量占整個系統的三分之一,傳回ArrayList<Form>的接口也有好多個,要改起來也是難,目前還沒啥好方法

得出結論就是:

1.終于知道為啥不能寫select * from table了,以前隻是見過這樣的建議

2.網上教程真的好少,千篇一律的介紹工具怎麼使用,沒有分析方法,我也是走到這裡進行不下去了,問人也都是不知道,其實這裡已經跟代碼跟業務有關了,可以憑着對系統的熟悉程度硬解析參考連接配接:

參考連結:

https://www.cnblogs.com/quyanhui/p/5924474.html

https://blog.csdn.net/forest_hou/article/details/5669427

https://blog.csdn.net/renfufei/article/details/77585294

http://www-01.ibm.com/support/docview.wss?uid=swg27006624&aid=1

http://java.sys-con.com/node/995699

http://java.sys-con.com/node/1229281

https://docs.oracle.com/javase/8/docs/technotes/samples/hprof.html