一個(gè)線上的問題,隨著時(shí)間推移,線程沒有正常關(guān)閉導(dǎo)致的線程泄漏,現(xiàn)象是服務(wù)器內(nèi)存趨于百分之百。
創(chuàng)新互聯(lián)公司于2013年成立,是專業(yè)互聯(lián)網(wǎng)技術(shù)服務(wù)公司,擁有項(xiàng)目做網(wǎng)站、網(wǎng)站建設(shè)網(wǎng)站策劃,項(xiàng)目實(shí)施與項(xiàng)目整合能力。我們以讓每一個(gè)夢(mèng)想脫穎而出為使命,1280元西吉做網(wǎng)站,已為上家服務(wù),為西吉各地企業(yè)和個(gè)人服務(wù),聯(lián)系電話:13518219792解決辦法: ????????1. 借助指令分析。使用 pslist -dmx pid配合jstack -l pid ,pslist結(jié)果如下圖。發(fā)現(xiàn)隨著時(shí)間推移(3分鐘增加一個(gè)),線程會(huì)越來愈多。而且線程的內(nèi)容會(huì)逐漸增加??梢詫栴}定位到定時(shí)任務(wù)(有個(gè)定時(shí)任務(wù)是3分鐘執(zhí)行一次),初步確定為由定時(shí)任務(wù)引發(fā)的問題。
2.查看jstack結(jié)果。如下圖,發(fā)現(xiàn)新增的線程都有個(gè)共性,都是由一個(gè)很陌生的類com.mashape.unirest.http.utils.SyncIdleConnectionMonitorThread 創(chuàng)建,于是在項(xiàng)目中全局搜這個(gè)類,通過搜com.mashape.unirest,找到了代碼所在位置。
3. 分析代碼。細(xì)看代碼,發(fā)現(xiàn)這是一個(gè)類似于發(fā)送http請(qǐng)求的工具類,只有這兩塊代碼。
這里有兩種解決辦法, 第一是查看源碼,分析原因,第二種就是直接調(diào)試代碼。
推薦用第二種(別問為什么,問就是因?yàn)楹?jiǎn)單、快),我當(dāng)時(shí)采用的是初中生物學(xué)到的控制變量法進(jìn)行的測(cè)試,保持其他條件不變,①注掉第104和105-112行相關(guān)代碼、②只注釋第104行代碼、③只注釋105-112行代碼。這三種情況測(cè)試完以后發(fā)現(xiàn)①、②兩種情況不會(huì)復(fù)現(xiàn)線程溢出的問題,于是定位到問題出在設(shè)置超時(shí)時(shí)間這行代碼上(104行)。
至此,問題就找到了,我們只需要把104這行代碼初始化一次,比如放進(jìn)靜態(tài)代碼塊,保證只會(huì)在類被加載的時(shí)候初始化一次,而不是每次執(zhí)行到這里就初始化,這樣就可以解決每次都會(huì)開啟線程的問題了。
4. 根本原因。當(dāng)然,作為一名優(yōu)秀的程序員,沒有查看源碼的好習(xí)慣明顯是不合格的(我怎么這么不要臉 - -?。ㄟ^分析源碼,截圖如下:
我發(fā)現(xiàn)Unirest.setTimeouts()這個(gè)方法內(nèi)部有一個(gè)refresh()的方法,點(diǎn)進(jìn)去看到有這么一坨代碼:
其中最長(zhǎng)的一行顯示不下了,我粘在這里:
RequestConfig clientConfig = RequestConfig.custom()
.setConnectTimeout(((Long)connectionTimeout).intValue())
.setSocketTimeout(((Long)socketTimeout).intValue())
.setConnectionRequestTimeout(((Long)socketTimeout).intValue())
.setProxy(proxy)
.build();
這塊代碼大概意思是,用我們之前setTimeouts方法參數(shù)里穿進(jìn)去的超時(shí)時(shí)間初始化客戶端的配置信息,之后將配置信息寫入連接池并初始化連接池的一些信息,最后會(huì)開啟一個(gè)名為SyncIdleConnectionMonitorThread的線程來處理這個(gè)連接池,看線程名字的意思,是一個(gè)監(jiān)測(cè)閑置鏈接的線程,就是清理資源的,看一看里面的東西:
他用了個(gè)死循環(huán)不斷監(jiān)視沒有關(guān)閉的線程,并獲取當(dāng)前線程對(duì)象的對(duì)象鎖,wait方法沒啥意義,就是為了防止循環(huán)太快搞死cup,每次都等5s再執(zhí)行。之后就是關(guān)閉資源的操作,然后繼續(xù)循環(huán),直到線程關(guān)閉時(shí),再執(zhí)行isInterruptes()方法就會(huì)報(bào)錯(cuò)拋異常,然后return退出線程。看起來好像沒什么問題,疑問就是我并不知道他會(huì)在哪里停止當(dāng)前線程,畢竟這個(gè)線程不會(huì)自己關(guān)閉,這樣就會(huì)一直循環(huán)下去,但是鬼知道哪里會(huì)停止當(dāng)前線程。
到這里還是沒有發(fā)現(xiàn)為什么,問題到底出在哪里?分析到這里的時(shí)候,我決定再細(xì)看一波代碼,當(dāng)然還是從setTimeouts這里看起,終于,在一行一行看源碼以后,粗心的我發(fā)現(xiàn)了問題,就在線程類的構(gòu)造方法里,有這么一行不起眼的代碼:super.setDaemon(true); 于是Google找了Thread.setDaemon(true)是啥意思:通過Thread.setDaemon(true)設(shè)置為守護(hù)線程,在沒有用戶線程可服務(wù)時(shí)會(huì)自動(dòng)離開。這里還很貼心的給出了實(shí)例,看樣子是說,只要主線程不死,守護(hù)線程就會(huì)一直存在。也就是說定時(shí)任務(wù)執(zhí)行一次,就會(huì)創(chuàng)建一個(gè)守護(hù)線程。
我和諧他和諧的和諧,這也太坑了吧!!
5. 總結(jié)這次主要是用了pslist 和jstack 指令查看線程相關(guān)信息(linux系統(tǒng)可以使用top配合jstack,也可以借助其他插件,比如ali的arthas,用起來挺不錯(cuò)),發(fā)現(xiàn)了異常線程,并通過這個(gè)信息定位到出錯(cuò)的代碼位置,這時(shí)就要考驗(yàn)分析代碼的基本功了。
辛辛苦苦分析了這么多,居然是這么個(gè)問題??梢姡谑褂脛e人的工具的時(shí)候,一定要好好查看api,或者多看幾個(gè)相關(guān)資料再下手,避免產(chǎn)生意想不到的問題。
另外,類似配置信息的這種東西,還是不要頻繁初始化的好,如果api里沒有詳細(xì)介紹使用細(xì)節(jié),可以看一看源碼,避免出錯(cuò)(時(shí)間充裕的情況下)。
參考:
JAVA Thread.setDaemon用法
你是否還在尋找穩(wěn)定的海外服務(wù)器提供商?創(chuàng)新互聯(lián)www.cdcxhl.cn海外機(jī)房具備T級(jí)流量清洗系統(tǒng)配攻擊溯源,準(zhǔn)確流量調(diào)度確保服務(wù)器高可用性,企業(yè)級(jí)服務(wù)器適合批量采購(gòu),新人活動(dòng)首月15元起,快前往官網(wǎng)查看詳情吧
網(wǎng)站標(biāo)題:一次線程溢出的實(shí)戰(zhàn)分析-創(chuàng)新互聯(lián)
網(wǎng)頁(yè)URL:http://www.rwnh.cn/article36/hdppg.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供微信公眾號(hào)、網(wǎng)站收錄、定制網(wǎng)站、品牌網(wǎng)站建設(shè)、外貿(mào)建站、品牌網(wǎng)站制作
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來源: 創(chuàng)新互聯(lián)
猜你還喜歡下面的內(nèi)容