常用Java靜態程式碼分析工具的分析與比較

Liuwei-Sunny發表於2012-09-09

簡介

      本文首先介紹了靜態程式碼分析的基本概念及主要技術,隨後分別介紹了4種現有的主流Java靜態程式碼分析工具 (Checkstyle,FindBugs,PMD,Jtest),最後從功能、特性等方面對它們進行分析和比較,希望能夠幫助Java軟體開發人員瞭解靜態程式碼分析工具,並選擇合適的工具應用到軟體開發中。

 

引言

      在Java軟體開發過程中,開發團隊往往要花費大量的時間和精力發現並修改程式碼缺陷。Java靜態程式碼分析(static code analysis)工具能夠在程式碼構建過程中幫助開發人員快速、有效的定位程式碼缺陷並及時糾正這些問題,從而極大地提高軟體可靠性並節省軟體開發和測試成本。目前市場上的Java靜態程式碼分析工具種類繁多且各有千秋,因此本文將分別介紹現有4種主流Java靜態程式碼分析工具(Checkstyle,FindBugs,PMD,Jtest),並從功能、特性等方面對它們進行分析和比較,希望能夠幫助Java軟體開發人員瞭解靜態程式碼分析工具,並選擇合適的工具應用到軟體開發中。

 

靜態程式碼分析工具簡介

什麼是靜態程式碼分析

      靜態程式碼分析是指無需執行被測程式碼,僅通過分析或檢查源程式的語法、結構、過程、介面等來檢查程式的正確性,找出程式碼隱藏的錯誤和缺陷,如引數不匹配,有歧義的巢狀語句,錯誤的遞迴,非法計算,可能出現的空指標引用等等。

      在軟體開發過程中,靜態程式碼分析往往先於動態測試之前進行,同時也可以作為制定動態測試用例的參考。統計證明,在整個軟體開發生命週期中,30%至70%的程式碼邏輯設計和編碼缺陷是可以通過靜態程式碼分析來發現和修復的。

      但是,由於靜態程式碼分析往往要求大量的時間消耗和相關知識的積累,因此對於軟體開發團隊來說,使用靜態程式碼分析工具自動化執行程式碼檢查和分析,能夠極大地提高軟體可靠性並節省軟體開發和測試成本。

靜態程式碼分析工具的優勢

      1. 幫助程式開發人員自動執行靜態程式碼分析,快速定位程式碼隱藏錯誤和缺陷。

      2. 幫助程式碼設計人員更專注於分析和解決程式碼設計缺陷。

      3. 顯著減少在程式碼逐行檢查上花費的時間,提高軟體可靠性並節省軟體開發和測試成本。

Java 靜態程式碼分析理論基礎和主要技術

  • 缺陷模式匹配:缺陷模式匹配事先從程式碼分析經驗中收集足夠多的共性缺陷模式,將待分析程式碼與已有的共性缺陷模式進行模式匹配,從而完成軟體的安全分析。這種方式的優點是簡單方便,但是要求內建足夠多缺陷模式,且容易產生誤報。
  • 型別推斷:型別推斷技術是指通過對程式碼中運算物件型別進行推理,從而保證程式碼中每條語句都針對正確的型別執行。這種技術首先將預定義一套型別機制,包括型別等價、型別包含等推理規則,而後基於這一規則進行推理計算。型別推斷可以檢查程式碼中的型別錯誤,簡單,高效,適合程式碼缺陷的快速檢測。
  • 模型檢查:模型檢驗建立於有限狀態自動機的概念基礎之上,這一理論將被分析程式碼抽象為一個自動機系統,並且假設該系統是有限狀態的、或者是可以通過抽象歸結為有限狀態。模型檢驗過程中,首先將被分析程式碼中的每條語句產生的影響抽象為一個有限狀態自動機的一個狀態,而後通過分析有限狀態機從而達到程式碼分析的目的。模型檢驗主要適合檢驗程式併發等時序特性,但是對於資料值域資料型別等方面作用較弱。
  • 資料流分析:資料流分析也是一種軟體驗證技術,這種技術通過收集程式碼中引用到的變數資訊,從而分析變數在程式中的賦值、引用以及傳遞等情況。對資料流進行分析可以確定變數的定義以及在程式碼中被引用的情況,同時還能夠檢查程式碼資料流異常,如引用在前賦值在後、只賦值無引用等。資料流分析主要適合檢驗程式中的資料域特性。

現有主流Java靜態分析工具

 Checkstyle

      Checkstyle 是SourceForge的開源專案,通過檢查對程式碼編碼格式,命名約定,Javadoc,類設計等方面進行程式碼規範和風格的檢查,從而有效約束開發人員更好地遵循程式碼編寫規範。

      Checkstyle 提供了支援大多數常見IDE的外掛,文字主要使用Eclipse中的Checkstyle外掛。如下圖1所示,Checkstyle 對程式碼進行編碼風格檢查,並將檢查結果顯示在 Problems 檢視中。圖中,程式碼編輯器中每個放大鏡圖示表示一個 Checkstyle 找到的程式碼缺陷。開發人員可通過在 Problems 檢視中檢視錯誤或警告詳細資訊。

圖1. 使用Checkstyle進行編碼風格檢查
圖 1. 使用 Checkstyle 進行編碼風格檢查

      此外,Checkstyle支援使用者根據需求自定義程式碼檢查規範,在下圖2中的配置皮膚中,使用者可以在已有檢查規範如命名約定,Javadoc,塊,類設計等方面的基礎上新增或刪除自定義檢查規範。

圖2. 使用Checkstyle新增自定義程式碼檢查規範
圖 2. 使用 Checkstyle 新增自定義程式碼檢查規範

FindBugs

      FindBugs 是由馬里蘭大學提供的一款開源 Java 靜態程式碼分析工具。FindBugs 通過檢查類檔案或 JAR 檔案,將位元組碼與一組缺陷模式進行對比從而發現程式碼缺陷,完成靜態程式碼分析。FindBugs 既提供視覺化 UI 介面,同時也可以作為 Eclipse 外掛使用。文字將主要使用將 FindBugs 作為 Eclipse 外掛。在安裝成功後會在 eclipse 中增加 FindBugs perspective,使用者可以對指定 Java 類或 JAR 檔案執行 FindBugs,此時 FindBugs 會遍歷指定檔案,進行靜態程式碼分析,並將程式碼分析結果顯示在 FindBugs perspective 的 bugs explorer 中,如下圖 3 所示:

圖3. 使用FindBugs進行靜態程式碼分析
圖 3. 使用 FindBugs 進行靜態程式碼分析

      圖中Bug Explorer中的灰色圖示處為 Bug 型別,每種分類下紅色圖示表示bug較為嚴重,黃色的圖示表示bug為警告程度。Propreties 列出了 bug 的描述資訊及修改方案。

      此外,FindBugs 還為使用者提供定製 Bug Pattern 的功能。使用者可以根據需求自定義 FindBugs 的程式碼檢查條件,如下圖 4 所示:

圖4. 使用 FindBugs 新增自定義程式碼檢查規範
圖 4. 使用 FindBugs 新增自定義程式碼檢查規範

PMD

      PMD 是由 DARPA 在 SourceForge 上釋出的開源 Java 程式碼靜態分析工具。PMD 通過其內建的編碼規則對 Java 程式碼進行靜態檢查,主要包括對潛在的 bug,未使用的程式碼,重複的程式碼,迴圈體建立新物件等問題的檢驗。PMD 提供了和多種 Java IDE 的整合,例如 Eclipse,IDEA,NetBean 等。本文主要使用 PMD 以外掛方式與 Eclipse 整合。如下圖 5 所示:在 Violations Overview 檢視中,按照程式碼缺陷嚴重性集中顯示了 PMD 靜態程式碼分析的結果。

圖5. 使用PMD進行靜態程式碼分析
圖 5. 使用 PMD 進行靜態程式碼分析

      PMD 同樣也支援開發人員對程式碼檢查規範進行自定義配置。開發人員可以在下圖 6 中的皮膚中新增、刪除、匯入、匯出程式碼檢查規範。

圖6. 使用PMD新增自定義程式碼檢查規範
圖 6. 使用 PMD 新增自定義程式碼檢查規範

Jtest

      Jtest 是Parasoft 公司推出的一款針對Java語言的自動化程式碼優化和測試工具,Jtest 的靜態程式碼分析功能能夠按照其內建的超過800條的 Java 編碼規範自動檢查並糾正這些隱蔽且難以修復的編碼錯誤。同時,還支援使用者自定義編碼規則,幫助使用者預防一些特殊用法的錯誤。Jtest 提供了基於 Eclipse 的外掛安裝。Jtest支援開發人員對Java程式碼進行編碼規範檢查,並在 Jtesk 視窗中集中顯示檢查結果,如下圖7所示:

圖7. 使用Jtest進行靜態程式碼分析
圖 7. 使用 Jtest 進行靜態程式碼分析

      同時,Jtest 還提供了對使用者定製程式碼檢查配置甚至自定義編碼規則的支援,這一功能使得開發人員可以基於不同場景定製所需要的編碼規範,如圖 8 所示:

圖8. 使用Jtest新增自定義程式碼檢查規範
圖 8. 使用 Jtest 新增自定義程式碼檢查規範

 

Java靜態分析工具對比

      本章節將從以下幾個方面對上述 Java 靜態分析工具進行比較:

應用技術及分析物件

      下表 1 列出了不同工具的分析物件及應用技術對比:

表1. 不同工具的分析物件及應用技術對比

Java 靜態分析工具分析物件應用技術
Checkstyle Java 原始檔缺陷模式匹配
FindBugs 位元組碼缺陷模式匹配;資料流分析
PMD Java 原始碼缺陷模式匹配
Jtest Java 原始碼缺陷模式匹配;資料流分析

內建程式設計規範

Checkstyle:

  • Javadoc 註釋:檢查類及方法的 Javadoc 註釋
  • 命名約定:檢查命名是否符合命名規範
  • 標題:檢查檔案是否以某些行開頭
  • Import 語句:檢查 Import 語句是否符合定義規範
  • 程式碼塊大小,即檢查類、方法等程式碼塊的行數
  • 空白:檢查空白符,如 tab,回車符等
  • 修飾符:修飾符號的檢查,如修飾符的定義順序
  • 塊:檢查是否有空塊或無效塊
  • 程式碼問題:檢查重複程式碼,條件判斷,魔數等問題
  • 類設計:檢查類的定義是否符合規範,如建構函式的定義等問題

FindBugs:

  • Bad practice 壞的實踐:常見程式碼錯誤,用於靜態程式碼檢查時進行缺陷模式匹配
  • Correctness 可能導致錯誤的程式碼,如空指標引用等
  • 國際化相關問題:如錯誤的字串轉換
  • 可能受到的惡意攻擊,如訪問許可權修飾符的定義等
  • 多執行緒的正確性:如多執行緒程式設計時常見的同步,執行緒排程問題。
  • 執行時效能問題:如由變數定義,方法呼叫導致的程式碼低效問題。

PMD:

  • 可能的 Bugs:檢查潛在程式碼錯誤,如空 try/catch/finally/switch 語句
  • 未使用程式碼(Dead code):檢查未使用的變數,引數,方法
  • 複雜的表示式:檢查不必要的 if 語句,可被 while 替代的 for 迴圈
  • 重複的程式碼:檢查重複的程式碼
  • 迴圈體建立新物件:檢查在迴圈體內例項化新物件
  • 資源關閉:檢查 Connect,Result,Statement 等資源使用之後是否被關閉掉

Jtest

  • 可能的錯誤:如記憶體破壞、記憶體洩露、指標錯誤、庫錯誤、邏輯錯誤和演算法錯誤等
  • 未使用程式碼:檢查未使用的變數,引數,方法
  • 初始化錯誤:記憶體分配錯誤、變數初始化錯誤、變數定義衝突
  • 命名約定:檢查命名是否符合命名規範
  • Javadoc 註釋:檢查類及方法的 Javadoc 註釋
  • 執行緒和同步:檢驗多執行緒程式設計時常見的同步,執行緒排程問題
  • 國際化問題:
  • 垃圾回收:檢查變數及 JDBC 資源是否存在記憶體洩露隱患

錯誤檢查能力

      為比較上述 Java 靜態分析工具的程式碼缺陷檢測能力,本文將使用一段示例程式碼進行試驗,示例程式碼中將涵蓋我們開發中的幾類常見錯誤,如引用操作、物件操作、表示式複雜化、數 組使用、未使用變數或程式碼段、資源回收、方法呼叫及程式碼設計幾個方面。最後本文將分別記錄在預設檢查規範設定下,不同工具對該示例程式碼的分析結果。以下為 示例程式碼 Test.java。其中,程式碼的註釋部分列舉了程式碼中可能存在的缺陷。

清單1. Test.java 示例程式碼

package Test;
import java.io.*;
public class Test {
	/**
	 * Write the bytes from input stream to output stream.
	 * The input stream and output stream are not closed.
	 * @param is
	 * @param os
	 * @throws IOException
	 */
	public  boolean copy(InputStream is, OutputStream os) throws IOException {
        int count = 0;
        //缺少空指標判斷
        byte[] buffer = new byte[1024];
		while ((count = is.read(buffer)) >= 0) {
			os.write(buffer, 0, count);
		}
		//未關閉I/O流
		return true;
	}
	/**
	 * 
	 * @param a
	 * @param b
	 * @param ending
	 * @return copy the elements from a to b, and stop when meet element ending
	 */
	public void copy(String[] a, String[] b, String ending)
	{
		int index;
		String temp = null;
		//空指標錯誤
		System.out.println(temp.length());
		//未使用變數
		int length=a.length;
		for(index=0; index < a.length; index++)
		{
			//多餘的if語句
			if(true)
			{
				//物件比較 應使用equals
				if(temp==ending)
				{
					break;
				}
				//缺少 陣列下標越界檢查
				b[index]=temp;
			}
		}
	}
	/**
	 * 
	 * @param file
	 * @return file contents as string; null if file does not exist
	 */
	public  void  readFile(File file) {
		InputStream is = null;
		OutputStream os = null;
			try {
				is = new BufferedInputStream(new FileInputStream(file));
				os = new ByteArrayOutputStream();
				//未使用方法返回值
				copy(is,os);
				is.close();
				os.close();
			} catch (IOException e) {
				//可能造成I/O流未關閉
				e.printStackTrace();
			} 
			finally
			{
				//空的try/catch/finally塊
			}
	}
}

      通過以上測試程式碼,我們對已有 Java 靜態程式碼分析工具的檢驗結果做了如下比較,如下表2所示。

表2. Java 靜態程式碼分析工具對比

程式碼缺陷分類 示例 Checkstyle FindBugs PMD Jtest
引用操作空指標引用
物件操作物件比較(使用 == 而不是 equals)
表示式複雜化多餘的 if 語句
陣列使用陣列下標越界
未使用變數或程式碼段未使用變數
資源回收I/O 未關閉
方法呼叫未使用方法返回值
程式碼設計空的 try/catch/finally 塊

     

      由表中可以看出幾種工具對於程式碼檢查各有側重。其中,Checkstyle 更偏重於程式碼編寫格式,及是否符合編碼規範的檢驗,對程式碼 bug 的發現功能較弱;而 FindBugs,PMD,Jtest 著重於發現程式碼缺陷。在對程式碼缺陷檢查中,這三種工具在針對的程式碼缺陷類別也各有不同,且類別之間有重疊。

 

總結

      本文分別從功能、特性和內建程式設計規範等方面詳細介紹了包括Checkstyle,FindBugs,PMD,Jtest在內的四種主流 Java 靜態程式碼分析工具,並通過一段Java程式碼示例對這四種工具的程式碼分析能力進行比較。由於這四種工具內建程式設計規範各有不同,因此它們對不同種類的程式碼問題的發現能力也有所不同。其中 Checkstyle 更加偏重於程式碼編寫格式檢查,而 FindBugs,PMD,Jtest 著重於發現程式碼缺陷。最後,希望本文能夠幫助 Java 軟體開發和測試人員進一步瞭解以上四種主流 Java 靜態分析工具,並幫助他們根據需求選擇合適的工具。 

 

      本文轉自http://www.oschina.net/question/129540_23043,特此說明!

相關文章