Name: sg39081 Date: 10/21/97
The following simple Java applications
demonstrate unusual, unexpected behavior
involving garbage collection. I wonder if this
behavior reveals the presence of bug(s) in Java.
I have included the Java source code, as well as
the output I get from each program. I am running
Win NT 4.0 on a Pentium Pro 200, with JDK 1.1.4.
MemoryTest1: The baseline, or "control", test.
Garbage-collection proceeds as expected.
MemoryTest2: Unusual behavior is produced,
if the code examines the args[] parameter as part
of a conditional expression.
MemoryTest3: Unusual behavior is produced,
if the code calls a do-nothing method of the
'Test' class.
For tests 2 and 3, normal behavior can be
restored by commenting out the indicated line(s)
of code. See the source code for details.
I am interested in feedback, especially from
JavaSoft. Is this a bug in Java? Or do I
misunderstand garbage collection? Although these
examples may seem pedantic, they are simplified
versions of similar problems we are encountering
in our large-scale Java Application development
effort.
Note: these are applications, not applets.
See the console output, below, for the
command-lines used to run these applications.
---Console Output---
C:\TEMP>javac MemoryTest1.java
C:\TEMP>java MemoryTest1
Both objects will be finalized, as expected.
(Even though none of the object references are re-assigned to null.)
'Test 1' constructed.
'Test 2' constructed.
'Test 1' finalized.
'Test 2' finalized.
C:\TEMP>javac MemoryTest2.java
C:\TEMP>java MemoryTest2 yes
Both objects will be finalized, as expected, IF the command-line argument is
'yes'.
BUT only one object will be finalized, IF the command-line argument is 'no'.
(This is unexpected behavior -- compare to results from MemoryTest1.)
'Test 1' constructed.
'Test 2' constructed.
Nullifying reference to 'Test 1'.
'Test 1' finalized.
'Test 2' finalized.
C:\TEMP>java MemoryTest2 no
Both objects will be finalized, as expected, IF the command-line argument is
'yes'.
BUT only one object will be finalized, IF the command-line argument is 'no'.
(This is unexpected behavior -- compare to results from MemoryTest1.)
'Test 1' constructed.
'Test 2' constructed.
'Test 2' finalized.
C:\TEMP>javac MemoryTest3.java
C:\TEMP>java MemoryTest3
ONLY ONE object will be finalized.
(This is unexpected behavior -- compare to results from MemoryTest1.)
'Object 1' constructed.
'Object 2' constructed.
Nullifying reference to 'Object 1'.
'Object 1' finalized.
C:\TEMP>
---MemoryTest1 Source Code---
public class MemoryTest1
{
public static void main(String args[])
{
System.out.println("Both objects will be finalized, as
expected.");
System.out.println("(Even though none of the object references
are re-assigned to null.)");
MyObject obj1 = new MyObject("Test 1");
MyObject obj2 = new MyObject("Test 2");
System.gc();
System.runFinalization();
}
}
class MyObject
{
String label;
MyObject(String label)
{
this.label = label;
System.out.println("'" + label + "' constructed.");
}
protected void finalize() throws Throwable
{
super.finalize();
System.out.println("'" + label + "' finalized.");
}
}
---MemoryTest2 Source Code---
public class MemoryTest2
{
public static void main(String args[])
{
System.out.println("Both objects will be finalized, as expected,
IF the command-line argument is 'yes'.");
System.out.println("BUT only one object will be finalized, IF
the command-line argument is 'no'.");
System.out.println("(This is unexpected behavior -- compare to
results from MemoryTest1.)");
MyObject obj1 = new MyObject("Test 1");
MyObject obj2 = new MyObject("Test 2");
/* switch the "ifs", below, to yield different behavior */
//if (true) { /*
this "if" yields expected behavior */
if (args.length > 0 && args[0].equalsIgnoreCase("yes")) { /*
this "if" triggers the unexpected behavior */
System.out.println("Nullifying reference to 'Test 1'.");
obj1 = null;
}
System.gc();
System.runFinalization();
}
}
class MyObject
{
String label;
MyObject(String label)
{
this.label = label;
System.out.println("'" + label + "' constructed.");
}
protected void finalize() throws Throwable
{
super.finalize();
System.out.println("'" + label + "' finalized.");
}
}
---MemoryTest3 Source Code---
public class MemoryTest3
{
public static void main(String args[])
{
System.out.println("ONLY ONE object will be finalized.");
System.out.println("(This is unexpected behavior -- compare to
results from MemoryTest1.)");
Test t = new Test();
// this call to this 'dummy' method triggers the unexpected
behavior.
// comment out this call, and both objects should get finalized.
t.dummy();
System.gc();
System.runFinalization();
}
}
class Test
{
MyObject c1;
MyObject c2;
Test()
{
c1 = new MyObject("Object 1");
c2 = new MyObject("Object 2");
System.out.println("Nullifying reference to 'Object 1'.");
c1 = null;
}
void dummy()
{
}
}
class MyObject
{
String label;
MyObject(String label)
{
this.label = label;
System.out.println("'" + label + "' constructed.");
}
protected void finalize() throws Throwable
{
super.finalize();
System.out.println("'" + label + "' finalized.");
}
}
======================================================================