最近公司的一臺服務器頻繁報警,老大讓我研究下代碼出了什么問題,咋一看才知道代碼是用大名鼎鼎的異步框架Vert.x寫的,本文記錄本菜鳥排查問題的辛酸過程,僅作為以后的一點經(jīng)驗參考。本服務器阿里云雙核ECS實例,2G內(nèi)存,CentOS7系統(tǒng)。
1.查看服務器硬盤內(nèi)存狀況
top #查看服務器內(nèi)存和硬盤容量

看了下參數(shù),以為這些挺重要的,其實發(fā)現(xiàn)CPU和Mem都很正常,內(nèi)存看似要用完了,其實free+buffers+cached大概容量也有900M,完全夠用。
2.查看服務器進程的堆棧
jcmd #查看進程,發(fā)現(xiàn)有兩個java項目,一個是公司的web項目,另一個是報警的Vertx項目
jinfo [pid] 查看堆內(nèi)存,發(fā)現(xiàn)一個項目的內(nèi)存參數(shù)-Xms1450M,本項目采用的默認配置,暫時看不出分配多少內(nèi)存
or ps aux | grep tomcat | grep -v grep 查看初始內(nèi)存使用
jstack [pid] 看棧信息,發(fā)現(xiàn)很多歌vert.thread在等待,因為服務器相應緩慢,看了下Log,看到了NPE
初步分析,NullPointerException導致了服務器相應緩慢。
首先調(diào)查,為什么會NullPointerException,根據(jù)Log很容易找到原因,最后配合前端同事知道新版本的前端請求格式不對造成報錯。
那么問題來了,報錯為什么會導致應用相應緩慢。可能是連接沒有釋放,或者是持久層插入錯誤數(shù)據(jù)報錯沒有釋放連接。
3 查看進程具體內(nèi)存
jinfo [pid] 查看到初始化的配置
看到Vertx最大堆內(nèi)存300M,old區(qū)只有50M,這個我很納悶,照理說默認配置新生區(qū)和old區(qū)比例是1:2起碼也有200M,這樣不會oom嗎。
jmap -heap [pid] 查看堆內(nèi)存各個分區(qū)大小,old區(qū)使用率90%以上,如果加大Old,如果還不行,肯定是內(nèi)存泄漏了,就是對象沒有釋放。
jstat -gcutil [pid] 1s 發(fā)現(xiàn)最近發(fā)生了6次FGC old區(qū)依然降不下去
果然過了一天,log就出現(xiàn)了heap Out Of Memory.
4 嘗試增加虛擬機內(nèi)存分配
javaweb項目部署在tomcat下,系統(tǒng)為它分配了1450M的內(nèi)存,打開tomcat/bin/catalina.sh,修改
JAVA_OPTS= -Xms1000M -Xmx1000M,這里保證初始分配內(nèi)存和最大堆內(nèi)存相同,放置系統(tǒng)調(diào)整內(nèi)存空間產(chǎn)生的消耗。
同時給報警的Vertx項目增加內(nèi)存,
java -Xms700M -Xmx700M -jar >>t.log 2>&1 & 將標準輸出重定向
參數(shù)說明如下
-Xms 初始化對內(nèi)存
-Xmx 最大堆內(nèi)存
-Xss每個線程內(nèi)存
-XX:PermSize設置非堆內(nèi)存初始值,默認是物理內(nèi)存的1/64
-XX:MaxPermSize設置最大非堆內(nèi)存的大小,默認是物理內(nèi)存的1/4
重啟項目后,過一段時間繼續(xù)查看
jmap -heap [pid] 發(fā)現(xiàn)堆內(nèi)存Old區(qū)飆升,還是達到了90%
5 查看代碼,檢查內(nèi)存泄漏位置
jstat -gcutil [pid] 60000 查看從程序啟動到現(xiàn)在進行了多少FGC,查看下old區(qū)百分比
jmap -histo:live [pid] >f 打印堆內(nèi)存存活對象信息
vi f
通過查看對象信息,從上往下發(fā)現(xiàn)有個對象EventItem被實例化了10w+次,這個類里面包含的數(shù)組,對象數(shù)組響應實例化次數(shù)也和其他不在同一數(shù)量級。確定是這個問題,回到代碼中,我找到了初始化該對象的位置,應該是報錯之后,request沒有釋放,request里面的private對象UploadRequest沒有釋放,導致錯誤的EventItem也無法釋放。
于是我在出錯的try.catch語句中直接將整個請求return,因為這些數(shù)據(jù)其實都是些無效的數(shù)據(jù),直接return也可以節(jié)約不少開銷。
后續(xù)觀察
修改代碼后,重新發(fā)布,已近觀察3天,出錯項目的heap的old區(qū)使用率終于降下來了,dump了對象信息也沒有大量的EventItem,服務器運行終于正常,說明問題解決了。這個過程中經(jīng)歷了一些坑,比如錯誤的以為是另外一個項目影響造成,浪費了不少時間排查,不過學到不少東西。
jinfo/jmap/jstat這些命令是調(diào)優(yōu)時候常用的,dump出文件查看對象信息,jconsole監(jiān)控本地jvm運行狀況,通過修改jvm參數(shù)調(diào)整服務響應時間等等。
代碼沒有貼出來,具體信息也沒細說,因為涉及公司項目還是保密為好,文章就留著以后回顧下吧。
個人博客 歡迎訪問~
祝大家編碼愉快,工作愉快,歡迎關(guān)注我的公眾號,一起分享交流
