java – 是否真的有必要在JUnit拆卸方法中清除对象?

参见英文答案 > JUnit – should I assign null to resources in tearDown that were instantiated in setUp?                                    3
我对answer感兴趣的是一个类似的问题.我相信这是不正确的.所以我创建了一些测试代码.我的问题是,这个代码是否证明/反驳/不确定这样一个假设,它有助于在拆卸方法中取消成员变量?我用JUnit4.8.1测试了它.

JUnit为4个测试中的每一个创建一个新的测试类实例.每个实例都包含一个Object obj.此obj也作为静态WeakHashMap的关键字插入.如果当JUnit释放其对测试实例的引用,则相关联的obj值将变得弱引用,因此符合gc.测试试图强制gc. WeakHashMap的大小将告诉我对象是否被gc’ed.一些测试使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’ed.但是Test1对象并没有被消除,并且在Test2中被gc了.这表明无效对象不是必需的.

JUnit 4.x样式测试和测试套件的处理方式与JUnit 3.x测试套件的处理方式不同.

简而言之,您应该在JUnit3样式测试中将字段设置为null,但不需要在JUnit4风格的测试中.

使用JUnit 3.x样式测试,TestSuite包含对其他Test对象(可能是TestCase对象或其他TestSuite对象)的引用.如果您创建了一个包含许多测试的套件,那么将会对最外层套件的整个运行的所有Leaf TestCase对象进行很多的参考.如果您的某些TestCase对象分配了占用大量内存的setUp()中的对象,并且对这些对象的引用存储在tearDown()中未设置为null的字段中,那么可能会出现内存问题.

换句话说,对于JUnit 3.x样式测试,运行哪些测试的规范引用了实际的TestCase对象.在测试运行期间,从TestCase对象可访问的任何对象都将保存在内存中.

对于JUnit 4.x样式测试,运行哪些测试的规范使用Description个对象. Description对象是一个值对象,它指定要运行的对象,但不指定如何运行它.测试由Runner对象运行,该对象接受测试或套件的描述,并确定如何执行测试.即使向测试监听器发送测试状态的通知也使用“描述”对象.

JUnit4测试用例的默认运行时间为JUnit4,仅在该测试运行期间保留对测试对象的引用.如果您使用自定义运行程序(通过@RunWith注释),该运行程序可能会或可能不会在较长时间内继续对测试进行引用.

也许你想知道如果在JUnit4样式Suite中包含JUnit3样式的测试类,会发生什么? JUnit4将调用新的TestSuite(Class),它将为每个测试方法创建一个单独的TestCase实例.赛跑者将在测试运行的整个生命周期中保持对TestSuite的引用.

简而言之,如果您正在编写JUnit4样式的测试,那么不用担心将测试用例的字段设置为null(当然,免费资源).如果您正在编写在setUp()中分配大对象的JUnit3样式测试,并将这些对象存储在TestCase的字段中,请考虑将字段设置为null.

翻译自:https://stackoverflow.com/questions/3655944/is-it-really-necessary-to-nullify-objects-in-junit-teardown-methods

转载注明原文:java – 是否真的有必要在JUnit拆卸方法中清除对象?