Java Q&A
by Ethan Henry and Ed Lycklama
Example 1:
Public void useless() {
MyObject m1 = new MyObject();
MyObject m2 = new MyObject();
m1.ref = m2;
global.ref = m2;
return;
}
Example 2:
public class PrintService {
static PrintService singleton;
Printable target;
public PrintService getPrintService() {
return singleton;
}
public void setTarget(Printable p) {
target = p;
}
public void doPrint() {
// set stuff up
// print target
}
}
Example 3:
Void method() {
// this creates a large object
Biggie big = readIt();
// this condenses it
Item item = findIt(big);
// we'd really like to reuse big's memory
// big = null;
// this method is going to run a long time
parseIt(item);
}
Listing One
/*****************************************************************************
* Copyright (c) 1999, KL GROUP INC. All Rights Reserved.
* http://www.klgroup.com
* The Software is provided "AS IS," without a warranty of any kind.
* ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES,
* INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A
* PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED.
* KL GROUP AND ITS LICENSORS SHALL NOT BE LIABLE FOR ANY DAMAGES
* SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR DISTRIBUTING
* THE SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL KL GROUP OR ITS
* LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT,
* INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES,
* HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT
* OF THE USE OF OR INABILITY TO USE SOFTWARE, EVEN IF KL GROUP HAS
* BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
*****************************************************************************/
import java.lang.reflect.*;
import java.util.*;
/*************************************************************
* Utility class for identifying loitering objects. Objects are tracked by
* calling ObjectTracker.add() when instantiated, and calling
* ObjectTracker.remove() when finalized. Only classes that implement
* ObjectTracker.Tracked can be tracked. As instances are created and
* destroyed, they are reported to the stdout. Summaries by class can also be
* reported on demand. To enable this functionality, add -DObjectTracker
* when running your program. This will track all classes that implement
* ObjectTracker.Tracked and call add/remove as indicated in the
* previous paragraph.
* For a finer degree of control, specify a list of filters
* when setting the ObjectTracker property. For instance,
* -DObjectTracker=+MySpecialClass,-ClassFoo will only report o
* on instances of classes whose name contains MySpecialClass
* but not ClassFoo. Hence MySpecialClassBar will be tracked, while
* MySpecialClassFoo will not be. See
* start() for more details.
* Limitations
* Since you must add instrumentation to all the classes you want to track,
* this is not nearly as useful as a Memory Profiler/Debugger like
* JProbe Profiler. Also, since it cannot tell you which references
* are causing the object to loiter, it doesn't help you remove the loiterers.
* If you want to solve the problem, you really need to use a Memory
* Profiler/Debugger like JProbe Profiler. The only thing ObjectTracker can
* help with is testing whether an instance of a known class goes away.
* Implementation Notes
* The current implementation assumes that every object has a unique
* hashcode. A false assumption in general, but does work in JavaSoft's Win32
* VM for JDK1.1. This implementation will definitely not work in JavaSoft's
* implementation of the Java 2 VM, including the HotSpot VM.
************************************************************* */
public class ObjectTracker {
// Property ObjectTracker turns this on when set
private final static boolean ENABLED =
System.getProperty("ObjectTracker") != null;
// Classes are hashed by name into this table.
private static Hashtable classReg;
private static Vector patterns;
/** Record info about an object. Class and ordinal number are stored. */
private static class ObjectEntry {
int ordinal; // distinguishes between mult. instances
String clazz; // classname
String name; // name (may be null)
public ObjectEntry(int ordinal, String clazz, String name) {
this.ordinal = ordinal;
this.clazz = clazz;
this.name = name;
}
public String toString() {
return clazz + ":#" + ordinal + " ("+name+")";
}
} // ObjectEntry
/** Records info about a class. Within each class, a table of objects is
* maintained, along with next ordinal to use to stamp next object
* of this class. */
private static class ClassEntry {
String clazz; // class name
Hashtable objects; // list of ObjectEntry
int ordinal; // last instance of this class created
public ClassEntry(String clazz) {
this.clazz = clazz;
objects = new Hashtable();
ordinal = 1;
}
public String toString() {
return clazz;
}
/** Get the name of the object by invoking getName().
* Uses reflection to find the method. */
private String getName(Object o) {
String name = null;
try {
Class cl = o.getClass();
Method m = cl.getMethod("getName", null);
name = (m.invoke(o, null)).toString();
}
catch (Exception e) { }
return name;
}
public void addObject(Object obj) {
// Store this object in the object table
Integer id = new Integer(System.identityHashCode(obj));
ObjectEntry entry = new ObjectEntry(ordinal, clazz, getName(obj));
objects.put(id, entry);
ordinal++;
System.out.println(" added: " +entry);
}
public void removeObject(Object obj) {
// Removes this object from the object table
Integer id = new Integer(System.identityHashCode(obj));
ObjectEntry entry = (ObjectEntry) objects.get(id);
objects.remove(id);
System.out.println(" removed: " +entry);
}
/** Dump out a list of all object in this table */
public void listObjects() {
if (objects.size() == 0) {
// skip empty tables
return;
}
System.out.println("For class: " + clazz);
Enumeration objs = objects.elements();
while (objs.hasMoreElements()) {
ObjectEntry entry = (ObjectEntry) objs.nextElement();
System.out.println(" " +entry);
}
}
} // ClassEntry
/** No constructor */
private ObjectTracker() {}
/** Determine is this class name should be tracked.
* @return true if this class should be tracked. @see start */
private static boolean isIncluded(String clazz) {
int i=0, size = patterns.size();
if (size == 0) {
// always match if list is empty
return true;
}
boolean flag = false;
for (; iObjectTracker is set. In addition, the list of
* patterns assigned to this property is stored for future pattern matching
* by isIncluded(). This list of patterns must be supplied as a
* comma-separated list, each preceded by + or -,
* which indicates whether or not the pattern should cause matching classes to
* be tracked or not. If property ObjectTracker has no values,
* it is equivalent to +all. */
public static void start() {
if (ENABLED) {
classReg = new Hashtable();
patterns = new Vector();
String targets = System.getProperty("ObjectTracker");
StringTokenizer parser = new StringTokenizer(targets, ",");
while (parser.hasMoreTokens()) {
String token = parser.nextToken();
patterns.addElement(token);
}
}
}
/** Add object to the tracked list. Will only be added if object's class has
* not been filtered out. @param obj object to be added to tracking list */
public static void add(Tracked obj) {
if (ENABLED) {
String clazz = obj.getClass().getName();
if (isIncluded(clazz)) {
ClassEntry entry = (ClassEntry) classReg.get(clazz);
if (entry == null) {
// first one for this class
entry = new ClassEntry(clazz);
classReg.put(clazz, entry);
}
entry.addObject(obj);
}
}
}
/** Removes object from tracked list. This method should be called
* from the finalizer. @param obj object to be removed from tracking list */
public static void remove(Tracked obj) {
if (ENABLED) {
String clazz = obj.getClass().getName();
if (isIncluded(clazz)) {
ClassEntry entry = (ClassEntry) classReg.get(clazz);
entry.removeObject(obj);
}
}
}
/** Print tracked objects, summarized by class. Also prints a
* summary of free/total memory. */
public static void dump() {
if (ENABLED) {
Enumeration e = classReg.elements();
while (e.hasMoreElements()) {
ClassEntry entry = (ClassEntry) e.nextElement();
entry.listObjects();
}
System.out.println("==================================");
System.out.println("Total Memory: " +
Runtime.getRuntime().totalMemory());
System.out.println("Free Memory: " +
Runtime.getRuntime().freeMemory());
System.out.println("==================================");
System.out.println("");
}
}
/** All classes that want to use this service must implement this
* interface. This forces this class to implement Object's finalize
* method, which should call ObjectTracker.remove(). */
public interface Tracked {
/** All classes that use ObjectTracker must implement a finalizer. */
void finalize();
}
}
Listing Two
/***************************************************************************
* Copyright (c) 1999, KL GROUP INC. All Rights Reserved.
* http://www.klgroup.com
* The Software is provided "AS IS," without a warranty of any kind.
* ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES,
* INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A
* PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED.
* KL GROUP AND ITS LICENSORS SHALL NOT BE LIABLE FOR ANY DAMAGES
* SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR DISTRIBUTING
* THE SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL KL GROUP OR ITS
* LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT,
* INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES,
* HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT
* OF THE USE OF OR INABILITY TO USE SOFTWARE, EVEN IF KL GROUP HAS
* BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
***************************************************************************/
public class tester implements ObjectTracker.Tracked {
private int[] junk = new int[5000];
public static void main(String args[]) {
ObjectTracker.start();
for (int i=0; i<1000; i++) {
tester t = new tester();
t.doNothing();
if (i%100 == 0) {
System.gc();
}
}
ObjectTracker.dump();
}
public tester() {
ObjectTracker.add(this);
}
public void finalize() {
ObjectTracker.remove(this);
}
public void doNothing() {
}
}
4