FULL PRODUCT VERSION :
java version "1.5.0_04"
Java(TM) 2 Runtime Environment, Standard Edition (build 1.5.0_04-b05)
Java HotSpot(TM) Client VM (build 1.5.0_04-b05, mixed mode)
ADDITIONAL OS VERSION INFORMATION :
Microsoft Windows XP [Version 5.1.2600]
A DESCRIPTION OF THE PROBLEM :
The Reference classes are not defined final, and thus one would infer that they can be derived from to effect enqueue behavior. This is not the case. Reference classes should either be marked final, to denote that they use voodoo, or the methods of the class should be use normal inheritance rules.
STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
This class does not work, unless poll is explicitly called, but should if normal inheritance rules applied: (assume normal listener pattern)
package com.mebigfatguy.gcnotifier;
import java.lang.ref.Reference;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.WeakReference;
import java.util.HashSet;
import java.util.Set;
public class GCNotifier {
private Set<GCListener> listeners = new HashSet<GCListener>();
private ReferenceQueue<Object> rq = new ReferenceQueue<Object>();
private Reference<Object> reference;
private Object beginEndSync = new Object();
private Object sync = new Object();
private Thread t = null;
public void begin() {
synchronized(beginEndSync) {
if (t != null)
return;
t = new Thread(new Runnable() {
public void run() {
try {
while (!Thread.interrupted()) {
synchronized(sync) {
sync.wait(); //If this is removed, and poll is done directly, it works
if (rq.poll() != null) {
notifyListeners();
resetNotifier();
}
}
}
} catch (InterruptedException ie) {
}
}
});
t.setDaemon(true);
t.start();
resetNotifier();
}
}
public void end() {
synchronized(beginEndSync) {
if (t == null)
return;
try {
t.interrupt();
t.join();
} catch (InterruptedException ie) {
}
t = null;
}
}
public void addGCListener(GCListener listener) {
synchronized(listeners) {
listeners.add(listener);
}
}
public void removeGCListener(GCListener listener) {
synchronized(listeners) {
listeners.remove(listener);
}
}
private void resetNotifier() {
Object referent = new Object();
reference = new WeakReference<Object>(referent, rq) {
@Override
public boolean enqueue() {
System.out.println("enqueue called");
boolean enq = super.enqueue();
synchronized(sync) {
sync.notify();
}
return enq;
}
};
}
private void notifyListeners() {
GCEvent gce = new GCEvent(this);
synchronized(sync) {
for (GCListener listener : listeners) {
listener.garbageCollectionOccurred(gce);
}
}
}
}
EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
the notify is never called, because enqueue is never called on the derived class.
ACTUAL -
enqueue should be called on the derived class.
REPRODUCIBILITY :
This bug can be reproduced always.
---------- BEGIN SOURCE ----------
// GCNotifier.java
package com.mebigfatguy.gcnotifier;
import java.lang.ref.Reference;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.WeakReference;
import java.util.HashSet;
import java.util.Set;
public class GCNotifier {
private Set<GCListener> listeners = new HashSet<GCListener>();
private ReferenceQueue<Object> rq = new ReferenceQueue<Object>();
private Reference<Object> reference;
private Object beginEndSync = new Object();
private Object sync = new Object();
private Thread t = null;
public void begin() {
synchronized(beginEndSync) {
if (t != null)
return;
t = new Thread(new Runnable() {
public void run() {
try {
while (!Thread.interrupted()) {
synchronized(sync) {
sync.wait(); //If this is removed, and poll is done directly, it works
if (rq.poll() != null) {
notifyListeners();
resetNotifier();
}
}
}
} catch (InterruptedException ie) {
}
}
});
t.setDaemon(true);
t.start();
resetNotifier();
}
}
public void end() {
synchronized(beginEndSync) {
if (t == null)
return;
try {
t.interrupt();
t.join();
} catch (InterruptedException ie) {
}
t = null;
}
}
public void addGCListener(GCListener listener) {
synchronized(listeners) {
listeners.add(listener);
}
}
public void removeGCListener(GCListener listener) {
synchronized(listeners) {
listeners.remove(listener);
}
}
private void resetNotifier() {
Object referent = new Object();
reference = new WeakReference<Object>(referent, rq) {
@Override
public boolean enqueue() {
System.out.println("enqueue called");
boolean enq = super.enqueue();
synchronized(sync) {
sync.notify();
}
return enq;
}
};
}
private void notifyListeners() {
GCEvent gce = new GCEvent(this);
synchronized(sync) {
for (GCListener listener : listeners) {
listener.garbageCollectionOccurred(gce);
}
}
}
}
// GCEvent.java
package com.mebigfatguy.gcnotifier;
import java.util.EventObject;
public class GCEvent extends EventObject {
public GCEvent(Object src) {
super(src);
}
}
//GCListener.java
package com.mebigfatguy.gcnotifier;
public interface GCListener {
public void garbageCollectionOccurred(GCEvent event);
}
//Test.java
package example;
import java.util.HashSet;
import java.util.Set;
import javax.swing.JOptionPane;
import com.mebigfatguy.gcnotifier.GCEvent;
import com.mebigfatguy.gcnotifier.GCListener;
import com.mebigfatguy.gcnotifier.GCNotifier;
public class Test {
public static void main(String[] args) {
GCNotifier gcn = new GCNotifier();
gcn.addGCListener(new GCListener() {
public void garbageCollectionOccurred(GCEvent gce) {
System.out.println("Garbage Collection Occurred");
}
});
gcn.begin();
try {
Set<byte[]> sb = new HashSet<byte[]>();
int fm = (int)Runtime.getRuntime().freeMemory();
while (fm > 0) {
Thread.sleep(100);
byte[] b = new byte[fm + 1];
sb.add(b);
fm = (int)Runtime.getRuntime().freeMemory();
}
} catch (Throwable th) {
System.out.println("Exception caught");
}
JOptionPane.showMessageDialog(null, "Quit?");
gcn.end();
}
}
---------- END SOURCE ----------
CUSTOMER SUBMITTED WORKAROUND :
cannot override reference classes with any effect.
java version "1.5.0_04"
Java(TM) 2 Runtime Environment, Standard Edition (build 1.5.0_04-b05)
Java HotSpot(TM) Client VM (build 1.5.0_04-b05, mixed mode)
ADDITIONAL OS VERSION INFORMATION :
Microsoft Windows XP [Version 5.1.2600]
A DESCRIPTION OF THE PROBLEM :
The Reference classes are not defined final, and thus one would infer that they can be derived from to effect enqueue behavior. This is not the case. Reference classes should either be marked final, to denote that they use voodoo, or the methods of the class should be use normal inheritance rules.
STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
This class does not work, unless poll is explicitly called, but should if normal inheritance rules applied: (assume normal listener pattern)
package com.mebigfatguy.gcnotifier;
import java.lang.ref.Reference;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.WeakReference;
import java.util.HashSet;
import java.util.Set;
public class GCNotifier {
private Set<GCListener> listeners = new HashSet<GCListener>();
private ReferenceQueue<Object> rq = new ReferenceQueue<Object>();
private Reference<Object> reference;
private Object beginEndSync = new Object();
private Object sync = new Object();
private Thread t = null;
public void begin() {
synchronized(beginEndSync) {
if (t != null)
return;
t = new Thread(new Runnable() {
public void run() {
try {
while (!Thread.interrupted()) {
synchronized(sync) {
sync.wait(); //If this is removed, and poll is done directly, it works
if (rq.poll() != null) {
notifyListeners();
resetNotifier();
}
}
}
} catch (InterruptedException ie) {
}
}
});
t.setDaemon(true);
t.start();
resetNotifier();
}
}
public void end() {
synchronized(beginEndSync) {
if (t == null)
return;
try {
t.interrupt();
t.join();
} catch (InterruptedException ie) {
}
t = null;
}
}
public void addGCListener(GCListener listener) {
synchronized(listeners) {
listeners.add(listener);
}
}
public void removeGCListener(GCListener listener) {
synchronized(listeners) {
listeners.remove(listener);
}
}
private void resetNotifier() {
Object referent = new Object();
reference = new WeakReference<Object>(referent, rq) {
@Override
public boolean enqueue() {
System.out.println("enqueue called");
boolean enq = super.enqueue();
synchronized(sync) {
sync.notify();
}
return enq;
}
};
}
private void notifyListeners() {
GCEvent gce = new GCEvent(this);
synchronized(sync) {
for (GCListener listener : listeners) {
listener.garbageCollectionOccurred(gce);
}
}
}
}
EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
the notify is never called, because enqueue is never called on the derived class.
ACTUAL -
enqueue should be called on the derived class.
REPRODUCIBILITY :
This bug can be reproduced always.
---------- BEGIN SOURCE ----------
// GCNotifier.java
package com.mebigfatguy.gcnotifier;
import java.lang.ref.Reference;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.WeakReference;
import java.util.HashSet;
import java.util.Set;
public class GCNotifier {
private Set<GCListener> listeners = new HashSet<GCListener>();
private ReferenceQueue<Object> rq = new ReferenceQueue<Object>();
private Reference<Object> reference;
private Object beginEndSync = new Object();
private Object sync = new Object();
private Thread t = null;
public void begin() {
synchronized(beginEndSync) {
if (t != null)
return;
t = new Thread(new Runnable() {
public void run() {
try {
while (!Thread.interrupted()) {
synchronized(sync) {
sync.wait(); //If this is removed, and poll is done directly, it works
if (rq.poll() != null) {
notifyListeners();
resetNotifier();
}
}
}
} catch (InterruptedException ie) {
}
}
});
t.setDaemon(true);
t.start();
resetNotifier();
}
}
public void end() {
synchronized(beginEndSync) {
if (t == null)
return;
try {
t.interrupt();
t.join();
} catch (InterruptedException ie) {
}
t = null;
}
}
public void addGCListener(GCListener listener) {
synchronized(listeners) {
listeners.add(listener);
}
}
public void removeGCListener(GCListener listener) {
synchronized(listeners) {
listeners.remove(listener);
}
}
private void resetNotifier() {
Object referent = new Object();
reference = new WeakReference<Object>(referent, rq) {
@Override
public boolean enqueue() {
System.out.println("enqueue called");
boolean enq = super.enqueue();
synchronized(sync) {
sync.notify();
}
return enq;
}
};
}
private void notifyListeners() {
GCEvent gce = new GCEvent(this);
synchronized(sync) {
for (GCListener listener : listeners) {
listener.garbageCollectionOccurred(gce);
}
}
}
}
// GCEvent.java
package com.mebigfatguy.gcnotifier;
import java.util.EventObject;
public class GCEvent extends EventObject {
public GCEvent(Object src) {
super(src);
}
}
//GCListener.java
package com.mebigfatguy.gcnotifier;
public interface GCListener {
public void garbageCollectionOccurred(GCEvent event);
}
//Test.java
package example;
import java.util.HashSet;
import java.util.Set;
import javax.swing.JOptionPane;
import com.mebigfatguy.gcnotifier.GCEvent;
import com.mebigfatguy.gcnotifier.GCListener;
import com.mebigfatguy.gcnotifier.GCNotifier;
public class Test {
public static void main(String[] args) {
GCNotifier gcn = new GCNotifier();
gcn.addGCListener(new GCListener() {
public void garbageCollectionOccurred(GCEvent gce) {
System.out.println("Garbage Collection Occurred");
}
});
gcn.begin();
try {
Set<byte[]> sb = new HashSet<byte[]>();
int fm = (int)Runtime.getRuntime().freeMemory();
while (fm > 0) {
Thread.sleep(100);
byte[] b = new byte[fm + 1];
sb.add(b);
fm = (int)Runtime.getRuntime().freeMemory();
}
} catch (Throwable th) {
System.out.println("Exception caught");
}
JOptionPane.showMessageDialog(null, "Quit?");
gcn.end();
}
}
---------- END SOURCE ----------
CUSTOMER SUBMITTED WORKAROUND :
cannot override reference classes with any effect.
- duplicates
-
JDK-4386925 (ref spec) Clarify specification to note that enqueue() and clear() are not invoked by the GC
-
- Resolved
-