2010-09-07 46 views
12

我被answer吸引到了類似的問題。我相信這是不正確的。所以我創建了一些測試代碼。我的問題是,這段代碼是否證明/反駁/不確定假設:在拆解方法中使成員變量無效是有用的假設?我用JUnit4.8.1測試了它。是否真的有必要在JUnit拆卸方法中取消對象?

JUnit爲4個測試中的每個測試創建一個測試類的新實例。每個實例都包含一個Object obj。這個obj也被插入爲靜態WeakHashMap的關鍵字。如果當JUnit釋放其對測試實例的引用時,關聯的obj值將變爲弱引用並因此符合gc。測試試圖強制一個gc。 WeakHashMap的大小會告訴我這個objs是否被gc化了。有些測試會使obj變量無效,而另一些則不會。

import org . junit . Before ; 
import org . junit . After ; 
import org . junit . Test ; 
import java . util . ArrayList ; 
import java . util . WeakHashMap ; 
import java . util . concurrent . atomic . AtomicInteger ; 
import static org . junit . Assert . * ; 

public class Memory 
{ 
    static AtomicInteger idx = new AtomicInteger (0) ; 

    static WeakHashMap < Object , Object > map = new WeakHashMap < Object , Object > () ; 

    int id ; 

    Object obj ; 

    boolean nullify ; 

    public Memory () 
    { 
    super () ; 
    } 

    @ Before 
    public void before () 
    { 
    id = idx . getAndIncrement () ; 
    obj = new Object () ; 
    map . put (obj , new Object ()) ; 
    System . out . println ("<BEFORE TEST " + id + ">") ; 
    } 

    void test (boolean n) 
    { 
    nullify = n ; 
    int before = map . size () ; 
    gc () ; 
    int after = map . size () ; 
    System . out . println ("BEFORE=" + before + "\tAFTER=" + after) ; 
    } 

    @ Test 
    public void test0 () 
    { 
    test (true) ; 
    } 

    @ Test 
    public void test1 () 
    { 
    test (false) ; 
    } 

    @ Test 
    public void test2 () 
    { 
    test (true) ; 
    } 

    @ Test 
    public void test3 () 
    { 
    test (false) ; 
    } 

    @ After 
    public void after () 
    { 
    if (nullify) 
     { 
     System . out . println ("Nullifying obj") ; 
     obj = null ; 
     } 
    System . out . println ("<AFTER TEST " + id + ">") ; 
    } 

    /** 
    * Try to force a gc when one is not really needed. 
    **/ 
    void gc () 
    { 
    ArrayList <Object> waste = new ArrayList <Object> () ; 
    System . gc () ; // only a suggestion but I'll try to force it 
    list : 
    while (true) // try to force a gc 
     { 
     try 
      { 
      waste . add (new Object ()) ; 
      } 
     catch (OutOfMemoryError cause) 
      { 
      // gc forced? should have been 
      waste = null ; 
      break list ; 
      } 
     } 
    System . gc () ; // only a suggestion but I tried to force it 
    } 
} 

我跑使用命令行接口(利用-Xmx128k選項來增加垃圾收集)的代碼,並得到了以下結果

.<BEFORE TEST 0> 
BEFORE=1 AFTER=1 
Nullifying obj 
<AFTER TEST 0> 
.<BEFORE TEST 1> 
BEFORE=2 AFTER=1 
<AFTER TEST 1> 
.<BEFORE TEST 2> 
BEFORE=2 AFTER=1 
Nullifying obj 
<AFTER TEST 2> 
.<BEFORE TEST 3> 
BEFORE=2 AFTER=1 
<AFTER TEST 3> 

的TEST0 OBJ被撤銷,在Test1的是GC 「編。但Test1 obj沒有被取消,它在Test2中得到了證明。這表明無效對象不是必需的。

回答

21

JUnit 4.x樣式測試和測試套件與JUnit 3.x測試套件的處理方式不同。

總之,你應該設置字段JUnit3式測試爲空,但你不需要在JUnit4風格的測試

在JUnit 3.x的風格的測試,一個TestSuite包含(其可以是TestCase對象或其它TestSuite對象)對其他Test對象的引用。如果你創建了一個包含很多測試的套件,那麼將會有對整個最外層套件運行的所有對象TestCase的硬引用。如果某些TestCase對象分配了佔用大量內存的setUp()中的對象,並且對這些對象的引用存儲在tearDown()中未設置爲null的字段中,則可能存在內存問題。

換句話說,對於JUnit 3.x樣式測試,運行哪些測試的規範引用了實際的TestCase對象。在測試運行期間,從TestCase對象可以到達的任何對象都將保存在內存中。

針對JUnit 4.x的風格測試,其說明書的測試運行使用Description對象。 Description對象是一個值對象,它指定要運行的內容,但不指示如何運行它。測試由Runner對象運行,該對象採用測試或套件的Description並確定如何執行測試。即使將測試狀態通知給測試監聽器也會使用Description對象。

JUnit4測試用例的默認運行程序JUnit4僅在測試運行期間保持對測試對象的引用。如果您使用自定義運動員(通過@RunWith註釋),該運動員可能會或可能不會將引用更長時間地保留在測試中。

也許你想知道如果在JUnit4風格Suite中包含JUnit3風格的測試類會發生什麼? JUnit4將會調用new TestSuite(Class),這將爲每個測試方法創建一個單獨的TestCase實例。跑步者將在測試運行的整個生命週期中保持對TestSuite的引用。總之,如果你正在編寫JUnit4風格的測試,不用擔心你的測試用例的字段設置爲null(當然是免費的資源)。如果你正在寫JUnit3式的測試,在setUp()TestCase領域分配大對象並存儲這些對象,考慮字段設置爲null

+1

這是最好的答案。在JUnit 3.x中,消除是必要的(以避免內存泄漏)。如果您使用JUnit4.x並且不需要默認運行程序無效化。如果您使用自定義亞軍,那麼您可能需要取消。 – emory 2010-09-08 00:37:27

0

確實沒有必要,但它確實有助於垃圾收集器,因爲它需要知道使用了哪些變量; null變量幾乎被認爲是垃圾收集的良好候選者。

+1

但在我的實驗中,GC收藏家總是設法弄清楚我是否不歸零。 – emory 2010-09-07 05:26:49

+0

你曾經使用過不同的垃圾收集器嗎?有些可能相當不錯,但有一些垃圾收集器需要一點幫助 – Drahakar 2010-09-07 06:00:18

+2

'它確實有助於垃圾收集器'。不,它不。整個對象在每次測試後被釋放。清零對象引用對最簡單的幫助沒有幫助。 – EJP 2010-09-07 08:48:34

0

不,這是沒有必要的。

拆卸方法是用於生命週期的對象,喜歡明確關閉,終止,關機,處置,斷開連接,未註冊或其他。

即使您的引用存活到下一個測試用例,它們也會被您的設置方法覆蓋併成爲未引用,因此符合垃圾回收的條件。

如果JUnit爲每個方法(這似乎是這種情況)創建了一個新的測試用例實例,那麼這些測試對象不會被保留。根據一項快速實驗,至少不能通過測試。無論如何,它的很多部分都會在閒暇時收集。

+0

兩個JUnit3風格測試和JUnit4風格創建測試被執行的每個測試方法一個單獨的對象,所以除非引用存儲在靜態(不推薦)的參考文獻中也不會由下一組向上法 – NamshubWriter 2010-09-07 16:32:20

+0

@NamshubWriter是被覆蓋保證或實施細節?如果後者可能會改變(儘管不太可能)。無論如何,這些實例並沒有被保留(在JUnit 4.8.1中,根據實驗) - 至少在測試通過時不會。 – 2010-09-07 20:40:58

+0

@NamshubWriter我更新了我的答案。感謝您的信息。 – 2010-09-07 20:50:04

相關問題