代码之家  ›  专栏  ›  技术社区  ›  emory

是否确实有必要在JUnit拆卸方法中取消对象?[复制品]

  •  14
  • emory  · 技术社区  · 14 年前

    我被 answer 类似的问题。我认为这是错误的。所以我创建了一些测试代码。我的问题是,这段代码是否证明/否定/不确定在拆卸方法中消除成员变量是有用的假设?我用JUnit4.8.1测试过。

    JUnit为4个测试中的每一个创建一个新的测试类实例。每个实例都包含一个对象obj。该对象也作为静态weakhashmap的键插入。如果JUnit释放对测试实例的引用,则关联的obj值将变为弱引用,从而符合GC。测试试图强制GC。weakhashmap的大小将告诉我obj是否已被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 obj没有无效,在test2中是gc'ed。这意味着不需要取消对象的无效性。

    3 回复  |  直到 9 年前
        1
  •  23
  •   NamshubWriter    9 年前

    JUnit4.x样式的测试和测试套件处理这一点与JUnit3.x测试套件不同。

    简而言之,你 在JUnit3样式测试中应将字段设置为空 但是 在JUnit4样式测试中不需要 .

    使用JUnit 3.x样式测试,a TestSuite 包含对其他的引用 Test 对象(可能是 TestCase 对象或其他 测试套件 对象)。如果您创建一个包含许多测试的套件,那么将有对所有叶的硬引用 测试用例 最外层套件的整个运行的对象。如果某些测试用例对象在 setUp() 占用大量内存,对这些对象的引用存储在未设置为的字段中 null 在里面 tearDown() ,那么您可能有内存问题。

    换句话说,对于JUnit3.x样式的测试,运行测试的规范引用了 测试用例 物体。任何可从 测试用例 对象将在测试运行期间保存在内存中。

    对于JUnit4.x样式的测试,运行测试的规范使用 Description 物体。这个 Description 对象是一个值对象,它指定运行什么,但不指定如何运行它。测试由 Runner 对象 描述 并确定如何执行测试。即使将测试状态通知给测试侦听器,也会使用 描述 物体。

    JUnit4测试用例的默认运行程序, JUnit4 ,仅在测试运行期间保留对测试对象的引用。如果使用自定义跑步者(通过 @RunWith 注释),运行程序可能会或可能不会将对测试的引用保留更长的时间。

    也许您想知道如果在JUnit4样式中包含JUnit3样式的测试类会发生什么情况 Suite ?Junit4会打电话 new TestSuite(Class) 它将创建一个单独的 测试用例 每个测试方法的实例。跑步者将参考 测试套件 在整个测试运行期间。

    简而言之,如果您正在编写JUnit4样式的测试,那么不要担心将测试用例的字段设置为 无效的 在一个崩溃(当然,做免费资源)。如果您正在编写JUnit3样式的测试,该测试在 设置() 并将这些对象存储在 测试用例 ,考虑将字段设置为 无效的 .

        2
  •  0
  •   Drahakar    14 年前

    这确实不是必需的,但是当垃圾收集器需要知道使用了哪些变量或不使用哪些变量时,它确实有助于垃圾收集器;空变量被大量地保护起来,可以很好地作为垃圾收集的候选者。

        3
  •  0
  •   Chris Vest    14 年前

    不,没有必要。

    分解方法是针对生命周期对象的,这些对象喜欢显式关闭、终止、关闭、释放、断开连接、注销或其他。

    即使您的引用在下一个测试用例中仍然存在,它们也将被您的设置方法覆盖,并且变为未引用,因此可以进行垃圾收集。

    如果JUnit为每个方法(看起来是这样)创建了一个新的测试用例实例,那么这些测试对象就不会被保留。根据一个快速实验,如果测试通过,至少不会。所以不管怎么说,大部分都是在闲暇时收集的。