Posted on 2015-06-08 19:13
Justfly Shi 閱讀(3975)
評論(4) 編輯 收藏 所屬分類:
隨便寫寫
感冒了,在家里休息,打開電腦隨便看看,想起前兩天有人問的一個事情:“內(nèi)存溢出怎么分析和處理?”方案有很多了,基本上的思路就是獲取系統(tǒng)狀態(tài),內(nèi)存變化方向,內(nèi)存對象等之類的,profile,debug,jmx,dump等等。
我更想說的是,為什么會內(nèi)存溢出呢?
在我看來,干活有兩種方式:
- 沒想清楚了,貿(mào)貿(mào)然開干,然后各處救火各種解決問題
- 想清楚了再開干,無驚無險,安然做完
一般來說,我都是后者,所以真的很少碰到各種莫名其妙的問題,比如自己實(shí)現(xiàn)排序算法、在內(nèi)存中處理有100000個值的列表、不用第三個變量來交換兩個變量的值等等。
怎么避免內(nèi)存溢出
吐槽完畢,來說說對于內(nèi)存溢出這種事情是要怎么避免,我所謂的“想清楚了再開干”到底是怎么想清楚的。
首先內(nèi)存溢出的本質(zhì)是什么?“內(nèi)存使用超出了預(yù)期”。
那么要怎么避免呢?“預(yù)期內(nèi)存怎么使用,將其控制在內(nèi)存使用范圍呢”。
如何預(yù)期內(nèi)存的使用
Java程序中,內(nèi)存是被怎么占用的?被數(shù)據(jù)和對象占用的,數(shù)據(jù)和對象怎么來的?
- 應(yīng)用的輸入和輸出
- 第三方系統(tǒng)的輸入和輸出
- 應(yīng)用本身產(chǎn)生的數(shù)據(jù)
如何控制內(nèi)存的使用
應(yīng)用輸入和輸出怎么控制
- 控制允許輸入的線程數(shù),比如允許同時多少個線程提供服務(wù)
- 控制輸入的請求對象的大小和內(nèi)容,比如輸入時的所允許的緩沖區(qū)大小
- 輸出的線程數(shù)是和輸入的線程對應(yīng)的,如果是主動輸出的,那么就控制一下
- 輸出的服務(wù)對象的大小和內(nèi)容,比如你是文件服務(wù)器,那么設(shè)置一個輸出緩存,每10K就Flush一下。
第三方系統(tǒng)的輸入和輸出怎么控制
思路和應(yīng)用輸入和輸出怎么控制所說的是一樣的,控制線程數(shù),使用緩存控制。
應(yīng)用本身產(chǎn)生的數(shù)據(jù)怎么控制
思路也是一樣的,線程數(shù),緩存,再加上生存時間,對象池等等。
還有?
使用上述技巧和思路你就能控制好你應(yīng)用中的內(nèi)存了,但是所有的設(shè)計都是在風(fēng)險和滿足需求之間的平衡,如果再退一步,那么你需要考慮一下Java虛擬機(jī)中內(nèi)存各個區(qū)的使用了。你需要區(qū)分好哪些是常駐對象,哪些是臨時對象,新生代,舊生代,怎么安排你的虛擬機(jī)中各個內(nèi)存區(qū)的大小。希望你不需要用到,如果需要的話,可以看看《深入理解Java虛擬機(jī)》這本書。
來個例子
有這么一個應(yīng)用,它獲取客戶端的請求,驗(yàn)證請求的合法性,然后對于合法的請求,從第三方系統(tǒng)獲取文件內(nèi)容,并把文件內(nèi)容寫回給客戶端。
下面是一大片的空白,再滾動下去之前看我的設(shè)計之前,讀者可以就這個需求想想你會怎么處理。
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
應(yīng)用本身的的輸入和輸出的控制
- 同時服務(wù)數(shù)設(shè)置為可配置的屬性,來控制并行的服務(wù)數(shù)
- 請求只允許Get請求,請求內(nèi)容只有Header和URL,設(shè)置一個輸入Buffer
- 輸出,設(shè)置輸出緩存區(qū)的大小,可配置的,默認(rèn)情況下,每20K Flush一下。
第三方系統(tǒng)的輸入和輸出
第三方系統(tǒng)有兩個,一個是合法性驗(yàn)證的,一個是文件內(nèi)容的。
合法性驗(yàn)證的請求和輸出的內(nèi)容都比較小。但是考慮到其服務(wù)器的性能,把合法性驗(yàn)證的交互過程放到一個線程池中控制。這樣能避免合法性驗(yàn)證服務(wù)不過來的情況。
文件內(nèi)容第三方系統(tǒng)的輸入和輸出的控制
- 和合法性驗(yàn)證一樣,文件內(nèi)容第三方系統(tǒng)的輸入和輸出能力有限,將其訪問用線程池控制起來。
- 文件內(nèi)容第三方系統(tǒng)的請求訪問對象較小,不予控制
- 整個系統(tǒng)的最重點(diǎn)來了!文件內(nèi)容第三方系統(tǒng)的返回文件大小不可控制。所以使用緩存機(jī)制,每次讀入20K(可配置),然后寫給系統(tǒng)的客戶端,然后再讀20K,然后再寫,讓文件內(nèi)容從這個系統(tǒng)中像水一樣流過去。流不動了(第三方系統(tǒng)連接或者客戶端連接失敗)就讓本次服務(wù)失敗。這個流過去就是重點(diǎn)了,不管這個文件多大,每次請求只占用20K。
應(yīng)用本身產(chǎn)生的數(shù)據(jù)
最原始的需求中還有一個把文件內(nèi)容緩存在本地,然后可以多次寫給客戶端的這么一個想法,減少網(wǎng)絡(luò)等待。這個想法被我否決了,原因如下:
- 首先可以通過HTTP 304狀態(tài)碼來減少同一客戶端對同樣內(nèi)容的多次讀取。
- 如果不對這個文件內(nèi)容進(jìn)行管理,將導(dǎo)致硬盤空間溢出。如果對其管理,會添加很多的開發(fā)、運(yùn)維和設(shè)計的工作,得不償失。
總結(jié)
想清楚了再開干就是這個意思,花點(diǎn)時間系統(tǒng)的想想,想清楚了,前面多寫點(diǎn)代碼,多寫點(diǎn)單元測試,后面少點(diǎn)麻煩,不用救火,也不用加班。
吃飯去了。
再多啰嗦一句,《JUnit收費(fèi)課程》的第二節(jié)的材料已經(jīng)準(zhǔn)備好了,昨晚擔(dān)心自己的塞著鼻子,大家聽著難受,沒有錄。今天好多了,晚上錄一下,明天上午編輯審核,明天下午或者晚上大家應(yīng)該就能看到了。