淺談效能測試

動腦科技發表於2019-03-02

關於記憶體在一塊其實我並不是很想拿出來說,一般情況下記憶體這一塊都是可優化的,可以通過硬體資源或者調整一些系統或者應用系統的引數配置來進行優化。

很多同僚問到了“記憶體洩漏”和“記憶體溢位”,其實這個在baidu上就有解釋。而我們很多人經常會混淆了這兩個東西,在這裡我就簡單的引用一些資源說一下他們。

記憶體洩漏
  
  記憶體洩漏也稱作“儲存滲漏”,用動態儲存分配函式動態開闢的空間,在使用完畢後未釋放,結果導致一直佔據該記憶體單元。直到程式結束。(其實說白了就是該記憶體空間使用完畢之後未回收)即所謂記憶體洩漏。
  
  記憶體洩漏形象的比喻是“作業系統可提供給所有程式的儲存空間正在被某個程式榨乾”,最終結果是程式執行時間越長,佔用儲存空間越來越多,最終用盡全部儲存空間,整個系統崩潰。所以“記憶體洩漏”是從作業系統的角度來看的。這裡的儲存空間並不是指實體記憶體,而是指虛擬記憶體大小,這個虛擬記憶體大小取決於磁碟交換區設定的大小。由程式申請的一塊記憶體,如果沒有任何一個指標指向它,那麼這塊記憶體就洩漏。

表象

效能測試的時候發生記憶體洩漏的表象就是可用記憶體逐漸減少,一直不釋放。

記憶體洩漏分類

以發生的方式來分類,記憶體洩漏可以分為4類

常發性

發生記憶體洩漏的程式碼會被多次執行到,每次被執行的時候都會導致一塊記憶體洩漏。

偶發性

發生記憶體洩漏的程式碼只有在某些特定環境或操作過程下才會發生。常發性和偶發性是相對的。對於特定的環境,偶發性的也許就變成了常發性的。所以測試環境和測試方法對檢測記憶體洩漏至關重要。

一次性

發生記憶體洩漏的程式碼只會被執行一次,或者由於演算法上的缺陷,導致總會有一塊且僅一塊記憶體發生洩漏。比如,在類的建構函式中分配記憶體,在解構函式中卻沒有釋放該記憶體,所以記憶體洩漏只會發生一次。
隱式

程式在執行過程中不停的分配記憶體,但是直到結束的時候才釋放記憶體。嚴格的說這裡並沒有發生記憶體洩漏,因為最終程式釋放了所有申請的記憶體。但是對於一個伺服器程式,需要執行幾天、幾周甚至幾個月,不及時釋放記憶體也可能導致最終耗盡系統的所有記憶體。所以,我們稱這類記憶體洩漏為隱式記憶體洩漏。

危害

記憶體洩漏或者是說,資源耗盡後,系統會表現出什麼現象啊?

cpu資源耗盡:估計是機器沒有反應了,鍵盤,滑鼠,以及網路等等。在中了計算機病毒的裝置上非常常見。

程式id耗盡:沒法建立新的程式了,串列埠或者telnet都沒法建立了。

硬碟耗盡: 機器要死了,交換記憶體沒法用,日誌也沒法用了,死是很正常的。

記憶體洩漏或者記憶體耗盡:新的連線無法建立,free的記憶體比較少。發生記憶體洩漏的程式很多,但是要想產生一定的後果,就需要這個程式是無限迴圈的,是個服務程式。當然,核心也是無限迴圈的,所以,如果核心發生了記憶體洩漏,情況就更加不妙。

記憶體洩漏是一種很難定位和跟蹤的錯誤,目前還沒看到有什麼好用的工具(當然,使用者空間有一些工具,有靜態分析的,也會動態分析的,但是找核心的記憶體洩漏,沒有好的開源工具)。

記憶體洩漏和物件的引用計數有很大的關係,再加上c/c++都沒有自動的垃圾回收機制,如果沒有手動釋放記憶體,問題就會出現。如果要避免這個問題,還是要從程式碼上入手,良好的編碼習慣和規範,是避免錯誤的不二法門。

一般我們常說的記憶體洩漏是指堆記憶體的洩漏。

堆記憶體是指程式從堆中分配的,大小任意的(記憶體塊的大小可以在程式執行期決定),使用完後必須顯式釋放的記憶體。

應用程式一般使用malloc,realloc,new等函式從堆中分配到一塊記憶體,使用完後,程式必須負責相應的呼叫free或delete釋放該記憶體塊,否則,這塊記憶體就不能被再次使用,我們就說這塊記憶體洩漏了。

部分檢測工具

1.ccmalloc-Linux和Solaris下對C和C++程式的簡單的使用記憶體洩漏和malloc除錯庫。

2.Dmalloc-Debug Malloc Library.

3.Electric Fence-Linux分發版中由Bruce Perens編寫的malloc()除錯庫。

4.Leaky-Linux下檢測記憶體洩漏的程式。

5.LeakTracer-Linux、Solaris和HP-UX下跟蹤和分析C++程式中的記憶體洩漏。

6.MEMWATCH-由Johan Lindh編寫,是一個開放原始碼C語言記憶體錯誤檢測工具,主要是通過gcc的precessor來進行。

7.Valgrind-Debugging and profiling Linux programs, aiming at programs written in C and C++.

8.KCachegrind-A visualization tool for the profiling data generated by Cachegrind and Calltree.

9.IBM Rational PurifyPlus-幫助開發人員查明C/C++、託管.NET、Java和VB6程式碼中的效能和可靠性錯誤。PurifyPlus 將記憶體錯誤和洩漏檢測、應用程式效能描述、程式碼覆蓋分析等功能組合在一個單一、完整的工具包中。

10.ParasoftInsure++-針對C/C++應用的執行時錯誤自動檢測工具,它能夠自動監測C/C++程式,發現其中存在著的記憶體破壞、記憶體洩漏、指標錯誤和I/O等錯誤。並通過使用一系列獨特的技術(SCI技術和變異測試等),徹底的檢查和測試我們的程式碼,精確定位錯誤的準確位置並給出詳細的診斷資訊。能作為MicrosoftVisual C++的一個外掛執行。

11.Compuware DevPartner for Visual C++ BoundsChecker Suite-為C++開發者設計的執行錯誤檢測和除錯工具軟體。作為Microsoft Visual Studio和C++ 6.0的一個外掛執行。

12.Electric Software GlowCode-包括記憶體洩漏檢查,code profiler,函式呼叫跟蹤等功能。給C++和.Net開發者提供完整的錯誤診斷,和執行時效能分析工具包。

13.Compuware DevPartner Java Edition-包含Java記憶體檢測,程式碼覆蓋率測試,程式碼效能測試,執行緒死鎖,分散式應用等幾大功能模組。

14.Quest JProbe-分析Java的記憶體洩漏。

15.ej-technologies JProfiler-一個全功能的Java剖析工具,專用於分析J2SE和J2EE應用程式。它把CPU、執行緒和記憶體的剖析組合在一個強大的應用中。

16.BEAJRockit-用來診斷Java記憶體洩漏並指出根本原因,專門針對Intel平臺並得到優化,能在Intel硬體上獲得最高的效能。

記憶體溢位

(out of memory)通俗理解就是記憶體不夠,通常在執行大型軟體或遊戲時,軟體或遊戲所需要的記憶體遠遠超出了你主機內安裝的記憶體所承受大小,就叫記憶體溢位。此時軟體或遊戲就執行不了,系統會提示記憶體溢位,有時候會自動關閉軟體,重啟電腦或者軟體後釋放掉一部分記憶體又可以正常執行該軟體或遊戲一段時間。

表象

常見out of memory 錯誤

產生原因

記憶體溢位是指應用系統中存在無法回收的記憶體或使用的記憶體過多,最終使得程式執行要用到的記憶體大於虛擬機器能提供的最大記憶體。為了解決Java中記憶體溢位問題,我們首先必須瞭解Java是如何管理記憶體的。Java的記憶體管理就是物件的分配和釋放問題。

在Java中,記憶體的分配是由程式完成的,而記憶體的釋放是由垃圾收集器(GarbageCollection,GC)完成的,程式設計師不需要通過呼叫GC函式來釋放記憶體,因為不同的JVM實現者可能使用不同的演算法管理GC,有的是記憶體使用到達一定程度時,GC才開始工作,也有定時執行的,有的是中斷式執行GC。

但GC只能回收無用並且不再被其它物件引用的那些物件所佔用的空間。Java的記憶體垃圾回收機制是從程式的主要執行物件開始檢查引用鏈,當遍歷一遍後發現沒有被引用的孤立物件就作為垃圾回收。

引起記憶體溢位的原因有很多種,常見的有以下幾種:

(1)記憶體中載入的資料量過於龐大,如一次從資料庫取出過多資料;

(2)集合類中有對物件的引用,使用完後未清空,使得JVM不能回收;

(3)程式碼中存在死迴圈或迴圈產生過多重複的物件實體;

(4)使用的第三方軟體中的BUG;

(5)啟動引數設定的過小;

解決方法

記憶體溢位雖然很棘手,但也有相應的解決辦法,可以按照從易到難,一步步的解決。

第一步,就是修改JVM啟動引數,直接增加記憶體。這一點看上去似乎很簡單,但很容易被忽略。JVM預設可以使用的記憶體為64M,Tomcat預設可以使用的記憶體為128MB,對於稍複雜一點的系統就會不夠用。在某專案中,就因為啟動引數使用的預設值,經常報“OutOfMemory”錯誤。因此,-Xms,-Xmx引數一定不要忘記加。

第二步,檢查錯誤日誌,檢視“OutOfMemory”錯誤前是否有其它異常或錯誤。在一個專案中,使用兩個資料庫連線,其中專用於傳送簡訊的資料庫連線使用DBCP連線池管理,使用者為不將簡訊發出,有意將資料庫連線使用者名稱改錯,使得日誌中有許多資料庫連線異常的日誌,一段時間後,就出現“OutOfMemory”錯誤。經分析,這是由於DBCP連線池BUG引起的,資料庫連線不上後,沒有將連線釋放,最終使得DBCP報“OutOfMemory”錯誤。經過修改正確資料庫連線引數後,就沒有再出現記憶體溢位的錯誤。

檢視日誌對於分析記憶體溢位是非常重要的,通過仔細檢視日誌,分析記憶體溢位前做過哪些操作,可以大致定位有問題的模組。

第三步,安排有經驗的程式設計人員對程式碼進行走查和分析,找出可能發生記憶體溢位的位置。重點排查以下幾點:

(1)檢查程式碼中是否有死迴圈或遞迴呼叫。

(2)檢查是否有大迴圈重複產生新物件實體。

(3)檢查對資料庫查詢中,是否有一次獲得全部資料的查詢。一般來說,如果一次取十萬條記錄到記憶體,就可能引起記憶體溢位。這個問題比較隱蔽,在上線前,資料庫中資料較少,不容易出問題,上線後,資料庫中資料多了,一次查詢就有可能引起記憶體溢位。因此對於資料庫查詢儘量採用分頁的方式查詢。

(4)檢查List、MAP等集合物件是否有使用完後,未清除的問題。List、MAP等集合物件會始終存有對物件的引用,使得這些物件不能被GC回收。

第四步,使用記憶體檢視工具動態檢視記憶體使用情況。某個專案上線後,每次系統啟動兩天後,就會出現記憶體溢位的錯誤。這種情況一般是程式碼中出現了緩慢的記憶體洩漏,用上面三個步驟解決不了,這就需要使用記憶體檢視工具了。

記憶體檢視工具有許多,比較有名的有:Optimizeit Profiler、JProbeProfiler、JinSight和Java1.5的Jconsole等。它們的基本工作原理大同小異,都是監測Java程式執行時所有物件的申請、釋放等動作,將記憶體管理的所有

資訊進行統計、分析、視覺化。開發人員可以根據這些資訊判斷程式是否有記憶體洩漏問題。一般來說,一個正常的系統在其啟動完成後其記憶體的佔用量是基本穩定的,而不應該是無限制的增長的。持續地觀察系統執行時使用的記憶體的大小,可以看到在記憶體使用監控視窗中是基本規則的鋸齒形的圖線,如果記憶體的大小持續地增長,則說明系統存在記憶體洩漏問題。通過間隔一段時間取一次記憶體快照,然後對記憶體快照中物件的使用與引用等資訊進行比對與分析,可以找出是哪個類的物件在洩漏。

通過以上四個步驟的分析與處理,基本能處理記憶體溢位的問題。當然,在這些過程中也需要相當的經驗與敏感度,需要在實際的開發與除錯過程中不斷積累。

緩衝區溢位

為了便於理解,我們不妨打個比方。緩衝區溢位好比是將十磅的糖放進一個只能裝五磅的容器裡。一旦該容器放滿了,餘下的部分就溢位在櫃檯和地板上,弄得一團糟。由於計算機程式的編寫者寫了一些編碼,但是這些編碼沒有對目的區域或緩衝區——五磅的容器——做適當的檢查,看它們是否夠大,能否完全裝入新的內容——十磅的糖,結果可能造成緩衝區溢位的產生。

如果打算被放進新地方的資料不適合,溢得到處都是,該資料也會製造很多麻煩。但是,如果緩衝區僅僅溢位,這只是一個問題。到此時為止,它還沒有破壞性。當糖溢位時,櫃檯被蓋住。可以把糖擦掉或用吸塵器吸走,還櫃檯本來面貌。與之相對的是,當緩衝區溢位時,過剩的資訊覆蓋的是計算機記憶體中以前的內容。除非這些被覆蓋的內容被儲存或能夠恢復,否則就會永遠丟失。

在丟失的資訊裡有能夠被程式呼叫的子程式的列表資訊,直到緩衝區溢位發生。另外,給那些子程式的資訊——引數——也丟失了。這意味著程式不能得到足夠的資訊從子程式返回,以完成它的任務。就像一個人步行穿過沙漠。如果他依賴於他的足跡走回頭路,當沙暴來襲抹去了這些痕跡時,他將迷失在沙漠中。這個問題比程式僅僅迷失方向嚴重多了。入侵者用精心編寫的入侵程式碼(一種惡意程式)使緩衝區溢位,然後告訴程式依據預設的方法處理緩衝區,並且執行。此時的程式已經完全被入侵者操縱了。

入侵者經常改編現有的應用程式執行不同的程式。例如,一個入侵者能啟動一個新的程式,傳送祕密檔案(支票本記錄,口令檔案,或財產清單)給入侵者的電子郵件。這就好像不僅僅是沙暴吹了腳印,而且後來者也會踩出新的腳印,將我們的迷路者領向不同的地方,他自己一無所知的地方。

緩衝處理

由於緩衝區溢位是一個程式設計問題,所以只能通過修復被破壞的程式的程式碼而解決問題。如果你沒有原始碼,從上面“堆疊溢位攻擊”的原理可以看出,要防止此類攻擊,我們可以:

1、開放程式時仔細檢查溢位情況,不允許資料溢位緩衝區。由於程式設計和程式語言的原因,這非常困難,而且不適合大量已經在使用的程式;

2、使用檢查堆疊溢位的編譯器或者在程式中加入某些記號,以便程式執行時確認禁止黑客有意造成的溢位。問題是無法針對已有程式,對新程式來講,需要修改編譯器;

3、經常檢查你的作業系統和應用程式提供商的站點,一旦發現他們提供的補丁程式,就馬上下載並且應用在系統上,這是最好的方法。但是系統管理員總要比攻擊者慢一步,如果這個有問題的軟體是可選的,甚至是臨時的,把它從你的系統中刪除。舉另外一個例子,你屋子裡的門和窗戶越少,入侵者進入的方式就越少。

扯遠了,這些是安全性測試方面的內容。不過效能測試方面有時候也得注意這些內容。效能測試的時候如果應用程式不報錯,並且系統的可用記憶體不是在不斷減少,就可以認為記憶體正常。

相關文章