1、 內存泄露程序員
內存泄漏指因爲疏忽或錯誤形成程序未能釋放已經再也不使用的內存的狀況。內存泄漏並不是指內存在物理上的消失,而是應用程序分配某段內存後,因爲設計錯誤,失去了對該段內存的控制,於是形成了內存的浪費。內存泄漏與許多其餘問題有着類似的症狀,而且一般狀況下只能由那些能夠得到程序源代碼的程序員才能夠分析出來。然而,有很多人習慣於把任何不須要的內存使用的增長描述爲內存泄漏,即便嚴格意義上來講這是不許確的。
通常咱們常說的內存泄漏是指堆內存的泄漏。堆內存是指程序從堆中分配的,大小任意的(內存塊的大小能夠在程序運行期決定),使用完後必須顯示釋放的內存。應用程序通常使用malloc,realloc,new等函數從堆中分配到一塊內存,使用完後,程序必須負責相應的調用free或delete釋放該內存塊,不然,這塊內存就不能被再次使用,咱們就說這塊內存泄漏了。
內存泄漏能夠分爲4類:
1. 常發性內存泄漏。發生內存泄漏的代碼會被屢次執行到,每次被執行的時候都會致使一塊內存泄漏。
2. 偶發性內存泄漏。發生內存泄漏的代碼只有在某些特定環境或操做過程下才會發生。常發性和偶發性是相對的。對於特定的環境,偶發性的也許就變成了常發性的。因此測試環境和測試方法對檢測內存泄漏相當重要。
3. 一次性內存泄漏。發生內存泄漏的代碼只會被執行一次,或者因爲算法上的缺陷,致使總會有一塊僅且一塊內存發生泄漏。好比,在一個Singleton類的構造函數中分配內存,在析構函數中卻沒有釋放該內存。而Singleton類只存在一個實例,因此內存泄漏只會發生一次。
4. 隱式內存泄漏。程序在運行過程當中不停的分配內存,可是直到結束的時候才釋放內存。嚴格的說這裏並無發生內存泄漏,由於最終程序釋放了全部申請的內存。可是對於一個服務器程序,須要運行幾天,幾周甚至幾個月,不及時釋放內存也可能致使最終耗盡系統的全部內存。因此,咱們稱這類內存泄漏爲隱式內存泄漏。 算法
內存泄漏會由於減小可用內存的數量從而下降計算機的性能。最終,在最糟糕的狀況下,過多的可用內存被分配掉致使所有或部分設備中止正常工做,或者應用程序崩潰。
內存泄漏可能不嚴重,甚至可以被常規的手段檢測出來。在現代操做系統中,一個應用程序使用的常規內存在程序終止時被釋放。這表示一個短暫運行的應用程序中的內存泄漏不會致使嚴重後果。
在如下狀況,內存泄漏致使較嚴重的後果:
* 程序運行後置之不理,而且隨着時間的流失消耗愈來愈多的內存(好比服務器上的後臺任務,尤爲是嵌入式系統中的後臺任務,這些任務可能被運行後不少年內都置之不理);
* 新的內存被頻繁地分配,好比當顯示電腦遊戲或動畫視頻畫面時;
* 程序可以請求未被釋放的內存(好比共享內存),甚至是在程序終止的時候;
* 泄漏在操做系統內部發生;
* 泄漏在系統關鍵驅動中發生;
* 內存很是有限,好比在嵌入式系統或便攜設備中;
* 當運行於一個終止時內存並不自動釋放的操做系統(好比AmigaOS)之上,並且一旦丟失只能經過重啓來恢復。數據庫
2、內存溢出編程
內存溢出已是軟件開發歷史上存在了近40年的「老大難」問題,象在「紅色代碼」病毒事件中表現的那樣,它已經成爲黑客攻擊企業網絡的「罪魁禍首」。 如在一個域中輸入的數據超過了它的要求就會引起數據溢出問題,多餘的數據就能夠做爲指令在計算機上運行。據有關安全小組稱,操做系統中超過50%的安全漏洞都是由內存溢出引發的,其中大多數和微軟的技術有關。
爲了便於理解,咱們不妨打個比方。緩衝區溢出比如是將十磅的糖放進一個只能裝五磅的容器裏。一旦該容器放滿了,餘下的部分就溢出在櫃檯和地板上,弄得一團糟。因爲計算機程序的編寫者寫了一些編碼,可是這些編碼沒有對目的區域或緩衝區——五磅的容器——作適當的檢查,看它們是否夠大,可否徹底裝入新的內容——十磅的糖,結果可能形成緩衝區溢出的產生。若是打算被放進新地方的數據不適合,溢獲得處都是,該數據也會製造不少麻煩。可是,若是緩衝區僅僅溢出,這只是一個問題。到此時爲止,它尚未破壞性。當糖溢出時,櫃檯被蓋住。能夠把糖擦掉或用吸塵器吸走,還櫃檯原本面貌。與之相對的是,當緩衝區溢出時,過剩的信息覆蓋的是計算機內存中之前的內容。除非這些被覆蓋的內容被保存或可以恢復,不然就會永遠丟失。
在丟失的信息裏有可以被程序調用的子程序的列表信息,直到緩衝區溢出發生。另外,給那些子程序的信息——參數——也丟失了。這意味着程序不能獲得足夠的信息從子程序返回,以完成它的任務。就像一我的步行穿過沙漠。若是他依賴於他的足跡走回頭路,當沙暴來襲抹去了這些痕跡時,他將迷失在沙漠中。這個問題比程序僅僅迷失方向嚴重多了。入侵者用精心編寫的入侵代碼(一種惡意程序)使緩衝區溢出,而後告訴程序依據預設的方法處理緩衝區,而且執行。此時的程序已經徹底被入侵者操縱了。
入侵者常常改編現有的應用程序運行不一樣的程序。例如,一個入侵者能啓動一個新的程序,發送祕密文件(支票本記錄,口令文件,或財產清單)給入侵者的電子郵件。這就好像不只僅是沙暴吹了腳印,並且後來者也會踩出新的腳印,將咱們的迷路者領向不一樣的地方,他本身一無所知的地方。
緩衝區溢出的處理
你屋子裏的門和窗戶越少,入侵者進入的方式就越少……
因爲緩衝區溢出是一個編程問題,因此只能經過修復被破壞的程序的代碼而解決問題。若是你沒有源代碼,從上面「堆棧溢出攻擊」的原理能夠看出,要防止此類攻擊,咱們能夠:
一、開放程序時仔細檢查溢出狀況,不容許數據溢出緩衝區。因爲編程和編程語言的緣由,這很是困難,並且不適合大量已經在使用的程序;
二、使用檢查堆棧溢出的編譯器或者在程序中加入某些記號,以便程序運行時確認禁止黑客有意形成的溢出。問題是沒法針對已有程序,對新程序來說,須要修改編譯器;
三、常常檢查你的操做系統和應用程序提供商的站點,一旦發現他們提供的補丁程序,就立刻下載而且應用在系統上,這是最好的方法。可是系統管理員總要比攻擊者慢一步,若是這個有問題的軟件是可選的,甚至是臨時的,把它從你的系統中刪除。舉另一個例子,你屋子裏的門和窗戶越少,入侵者進入的方式就越少。tomcat
通俗理解就是內存不夠,一般在運行大型軟件或遊戲時,軟件或遊戲所須要的內存遠遠超出了你主機內安裝的內存所承受大小,就叫內存溢出。此時軟件或遊戲就運行不了,系統會提示內存溢出,有時候會自動關閉軟件,重啓電腦或者軟件後釋放掉一部份內存又能夠正常運行該軟件或遊戲一段時間。
含義
內存溢出已是軟件開發歷史上存在了近40年的「老大難」問題,像在「紅色代碼」病毒事件中表現的那樣,它已經成爲黑客攻擊企業網絡的「罪魁禍首」。 如在一個域中輸入的數據超過了它的要求就會引起數據溢出問題,多餘的數據就能夠做爲指令在計算機上運行。據有關安全小組稱,操做系統中超過50%的安全漏洞都是由內存溢出引發的,其中大多數與微軟的技術有關。
爲了便於理解,咱們不妨打個比方。緩衝區溢出比如是將十磅的糖放進一個只能裝五磅的容器裏。一旦該容器放滿了,餘下的部分就溢出在櫃檯和地板上,弄得一團糟。因爲計算機程序的編寫者寫了一些編碼,可是這些編碼沒有對目的區域或緩衝區——五磅的容器——作適當的檢查,看它們是否夠大,可否徹底裝入新的內容——十磅的糖,結果可能形成緩衝區溢出的產生。若是打算被放進新地方的數據不適合,溢獲得處都是,該數據也會製造不少麻煩。可是,若是緩衝區僅僅溢出,這只是一個問題。到此時爲止,它尚未破壞性。當糖溢出時,櫃檯被蓋住。能夠把糖擦掉或用吸塵器吸走,還櫃檯原本面貌。與之相對的是,當緩衝區溢出時,過剩的信息覆蓋的是計算機內存中之前的內容。除非這些被覆蓋的內容被保存或可以恢復,不然就會永遠丟失。
在丟失的信息裏有可以被程序調用的子程序的列表信息,直到緩衝區溢出發生。另外,給那些子程序的信息——參數——也丟失了。這意味着程序不能獲得足夠的信息從子程序返回,以完成它的任務。就像一我的步行穿過沙漠。若是他依賴於他的足跡走回頭路,當沙暴來襲抹去了這些痕跡時,他將迷失在沙漠中。這個問題比程序僅僅迷失方向嚴重多了。入侵者用精心編寫的入侵代碼(一種惡意程序)使緩衝區溢出,而後告訴程序依據預設的方法處理緩衝區,而且執行。此時的程序已經徹底被入侵者操縱了。
入侵者常常改編現有的應用程序運行不一樣的程序。例如,一個入侵者能啓動一個新的程序,發送祕密文件(支票本記錄,口令文件,或財產清單)給入侵者的電子郵件。這就好像不只僅是沙暴吹了腳印,並且後來者也會踩出新的腳印,將咱們的迷路者領向不一樣的地方,他本身一無所知的地方。
緩衝區溢出的處理 你屋子裏的門和窗戶越少,入侵者進入的方式就越少……
因爲緩衝區溢出是一個編程問題,因此只能經過修復被破壞的程序的代碼而解決問題。若是你沒有源代碼,從上面「堆棧溢出攻擊」的原理能夠看出,要防止此類攻擊,咱們能夠:
一、開放程序時仔細檢查溢出狀況,不容許數據溢出緩衝區。因爲編程和編程語言的緣由,這很是困難,並且不適合大量已經在使用的程序;
二、使用檢查堆棧溢出的編譯器或者在程序中加入某些記號,以便程序運行時確認禁止黑客有意形成的溢出。問題是沒法針對已有程序,對新程序來說,須要修改編譯器;
三、常常檢查你的操做系統和應用程序提供商的站點,一旦發現他們提供的補丁程序,就立刻下載而且應用在系統上,這是最好的方法。可是系統管理員總要比攻擊者慢一步,若是這個有問題的軟件是可選的,甚至是臨時的,把它從你的系統中刪除。舉另一個例子,你屋子裏的門和窗戶越少,入侵者進入的方式就越少。
問題的提出 內存溢出與數據庫鎖表的問題,能夠說是開發人員的噩夢,通常的程序異常,老是能夠知道在何時或是在什麼操做步驟上出現了異常,並且根據堆棧信息也很容易定位到程序中是某處出現了問題。內存溢出與鎖表則否則,通常現象是操做通常時間後系統愈來愈慢,直到死機,但並不能明確是在什麼操做上出現的,發生的時間點也沒有規律,查看日誌或查看數據庫也不能定位出問題的代碼。
更嚴重的是內存溢出與數據庫鎖表在系統開發和單元測試階段並不容易被發現,當系統正式上線通常時間後,操做的併發量上來了,數據也積累了一些,系統就容易出現內存溢出或是鎖表的現象,而此時系統又不能隨意停機或重啓,爲修正BUG帶來很大的困難。
本文以筆者開發和支持的多個項目爲例,與你們分享在開發過程當中遇到的Java內存溢出和數據庫鎖表的檢測和處理解決過程。
內存溢出的分析 內存溢出是指應用系統中存在沒法回收的內存或使用的內存過多,最終使得程序運行要用到的內存大於虛擬機能提供的最大內存。爲了解決Java中內存溢出問題,咱們首先必須瞭解Java是如何管理內存的。Java的內存管理就是對象的分配和釋放問題。在Java中,內存的分配是由程序完成的,而內存的釋放是由垃圾收集器(GarbageCollection,GC)完成的,程序員不須要經過調用GC函數來釋放內存,由於不一樣的JVM實現者可能使用不一樣的算法管理GC,有的是內存使用到達必定程度時,GC纔開始工做,也有定時執行的,有的是中斷式執行GC。但GC只能回收無用而且再也不被其它對象引用的那些對象所佔用的空間。Java的內存垃圾回收機制是從程序的主要運行對象開始檢查引用鏈,當遍歷一遍後發現沒有被引用的孤立對象就做爲垃圾回收。
引發內存溢出的緣由有不少種,常見的有如下幾種:
? 內存中加載的數據量過於龐大,如一次從數據庫取出過多數據;
? 集合類中有對對象的引用,使用完後未清空,使得JVM不能回收;
? 代碼中存在死循環或循環產生過多重複的對象實體;
? 使用的第三方軟件中的BUG;
? 啓動參數內存值設定的太小;
內存溢出的解決
內存溢出雖然很棘手,但也有相應的解決辦法,能夠按照從易到難,一步步的解決。 第一步,就是修改JVM啓動參數,直接增長內存。這一點看上去彷佛很簡單,但很容易被忽略。JVM默承認以使用的內存爲64M,Tomcat默承認以使用的內存爲128MB,對於稍複雜一點的系統就會不夠用。在某項目中,就由於啓動參數使用的默認值,常常報「OutOfMemory」錯誤。所以,-Xms,-Xmx參數必定不要忘記加。 第二步,檢查錯誤日誌,查看「OutOfMemory」錯誤前是否有其它異常或錯誤。在一個項目中,使用兩個數據庫鏈接,其中專用於發送短信的數據庫鏈接使用DBCP鏈接池管理,用戶爲不將短信發出,有意將數據庫鏈接用戶名改錯,使得日誌中有許多數據庫鏈接異常的日誌,一段時間後,就出現「OutOfMemory」錯誤。經分析,這是因爲DBCP鏈接池BUG引發的,數據庫鏈接不上後,沒有將鏈接釋放,最終使得DBCP報「OutOfMemory」錯誤。通過修改正確數據庫鏈接參數後,就沒有再出現內存溢出的錯誤。 查看日誌對於分析內存溢出是很是重要的,經過仔細查看日誌,分析內存溢出前作過哪些操做,能夠大體定位有問題的模塊。 第三步,安排有經驗的編程人員對代碼進行走查和分析,找出可能發生內存溢出的位置。重點排查如下幾點: ? 檢查代碼中是否有死循環或遞歸調用。 ? 檢查是否有大循環重複產生新對象實體。 ? 檢查對數據庫查詢中,是否有一次得到所有數據的查詢。通常來講,若是一次取十萬條記錄到內存,就可能引發內存溢出。這個問題比較隱蔽,在上線前,數據庫中數據較少,不容易出問題,上線後,數據庫中數據多了,一次查詢就有可能引發內存溢出。所以對於數據庫查詢儘可能採用分頁的方式查詢。 ? 檢查List、MAP等集合對象是否有使用完後,未清除的問題。List、MAP等集合對象會始終存有對對象的引用,使得這些對象不能被GC回收。 第四步,使用內存查看工具動態查看內存使用狀況。某個項目上線後,每次系統啓動兩天後,就會出現內存溢出的錯誤。這種狀況通常是代碼中出現了緩慢的內存泄漏,用上面三個步驟解決不了,這就須要使用內存查看工具了。 內存查看工具備許多,比較有名的有:Optimizeit Profiler、JProbeProfiler、JinSight和Java1.5的Jconsole等。它們的基本工做原理大同小異,都是監測Java程序運行時全部對象的申請、釋放等動做,將內存管理的全部信息進行統計、分析、可視化。開發人員能夠根據這些信息判斷程序是否有內存泄漏問題。通常來講,一個正常的系統在其啓動完成後其內存的佔用量是基本穩定的,而不該該是無限制的增加的。持續地觀察系統運行時使用的內存的大小,能夠看到在內存使用監控窗口中是基本規則的鋸齒形的圖線,若是內存的大小持續地增加,則說明系統存在內存泄漏問題。經過間隔一段時間取一次內存快照,而後對內存快照中對象的使用與引用等信息進行比對與分析,能夠找出是哪一個類的對象在泄漏。 經過以上四個步驟的分析與處理,基本能處理內存溢出的問題。固然,在這些過程當中也須要至關的經驗與敏感度,須要在實際的開發與調試過程當中不斷積累。