我对answer对类似问题很感兴趣。我认为这是不正确的。所以我创建了一些测试代码。我的问题是,这个代码是否证明/反驳/不确定这一假设,即在拆解方法中使成员变量无效是有用的?我用JUnit4.8.1进行了测试。
JUnit为4个测试中的每个测试创建一个新的测试类实例。每个实例都包含一个Object obj。此obj也作为静态WeakHashMap的键插入。如果JUnit释放对测试实例的引用,则关联的obj值将被弱引用,因此符合gc条件。测试试图强制gc。 WeakHashMap的大小将告诉我objs是否是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。这表明无需使用对象。
答案 0 :(得分:23)
JUnit 4.x样式测试和测试套件的处理方式与JUnit 3.x测试套件不同。
简而言之,应该在JUnit3样式的测试中将字段设置为null ,但在JUnit4样式的测试中不需要。
使用JUnit 3.x样式测试,TestSuite
包含对其他Test
个对象(可能是TestCase
个对象或其他TestSuite
个对象)的引用。如果您创建一个包含许多测试的套件,那么对于最外层套件的整个运行,将会有对所有叶TestCase
对象的硬引用。如果某些TestCase对象在setUp()
中分配占用大量内存的对象,并且对这些对象的引用存储在null
中未设置为tearDown()
的字段中,那么您可能有记忆问题。
换句话说,对于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 :(得分:0)
确实没有必要,但它确实有助于垃圾收集器何时需要知道使用了哪些变量; null变量非常适合垃圾收集。
答案 2 :(得分:0)
不是没有必要。
拆除方法适用于生命周期对象,这些对象喜欢显式关闭,终止,关闭,处置,断开连接,未注册或其他任何内容。
即使您的引用仍然存在于下一个测试用例中,它们也会被您的设置方法覆盖并变为未引用,因此有资格进行垃圾回收。
如果JUnit为每个方法创建了一个新的测试用例(似乎就是这种情况),那么这些测试对象就不会被保留。根据一项快速实验,至少没有测试通过。所以很多时候它都会在闲暇时收集。