1

I am struggling with the garbage collector. I want to list all the objects that are reachable strongly or weakly from a certain object. I know I need to do it recursively, but I can't find a simple way to achieve it. Could you please help me?

public static List<Object> getAllReachableObjects (Object from)
Artjom B.
  • 61,146
  • 24
  • 125
  • 222
user3572544
  • 189
  • 7
  • All objects? Like, objects on objects on fields etc? Otherwise, just reflectively check its fields. – Evan Knowles Apr 25 '14 at 10:41
  • Yes, all objects. I tried to check all fields by from.getClass ().getFields (), but I can't get it to work. Could you please provide some code showing what you mean? – user3572544 Apr 25 '14 at 10:44

2 Answers2

1

The straighforward solution should not be so difficult, but ... notice that there may be many objects reachable from a given object....

EDIT based on the comment:

The objects should be separated into softly-, weakly-, phantom- and strongly reachable objects. This is a bit more complicated. One could implement an elegant solution based on graphs manually, but I pragmatically modified the code from the first answer. Note that this has not been tested extensively.

import java.lang.ref.PhantomReference;
import java.lang.ref.Reference;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.SoftReference;
import java.lang.ref.WeakReference;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;

public class AllReachableObjects
{
    public static void main(String[] args)
    {
        TestObject a = new TestObject("a", null, null, null, null);
        TestObject as = new TestObject("as", a, null, null, null);
        TestObject aw = new TestObject("aw", null, a, null, null);
        TestObject ap = new TestObject("ap", null, null, a, null);
        TestObject ar = new TestObject("ar", null, null, null, a);

        printInfo(new ReachableObjects(as));
        printInfo(new ReachableObjects(aw));
        printInfo(new ReachableObjects(ap));
        printInfo(new ReachableObjects(ar));

        TestObject asr = new TestObject("as", null, null, null, as);
        TestObject ars = new TestObject("as", ar, null, null, null);

        printInfo(new ReachableObjects(asr));
        printInfo(new ReachableObjects(ars));

    }

    private static void printInfo(ReachableObjects r)
    {
        System.out.println("Soft");
        printList(r.getSoftlyReachable());
        System.out.println("Weak");
        printList(r.getWeaklyReachable());
        System.out.println("Phantom");
        printList(r.getPhantomReachable());
        System.out.println("Strong");
        printList(r.getStronglyReachable());
    }

    private static void printList(List<Object> list)
    {
        for (Object object : list)
        {
            System.out.println("    "+object+" (class "+object.getClass()+")");
        }
    }


}


class ReachableObjects
{
    private static final Field REFERENCE_REFERENT_FIELD =
        initReferenceReferentField();

    private static Field initReferenceReferentField()
    {
        try
        {
            return Reference.class.getDeclaredField("referent");
        }
        catch (NoSuchFieldException e)
        {
            e.printStackTrace();
        }
        catch (SecurityException e)
        {
            e.printStackTrace();
        }
        return null;
    }

    private Set<Object> softlyReachable;
    private Set<Object> weaklyReachable;
    private Set<Object> phantomReachable;
    private Set<Object> stronglyReachable;


    public ReachableObjects(Object object)
    {
        softlyReachable = new LinkedHashSet<Object>();
        weaklyReachable = new LinkedHashSet<Object>();
        phantomReachable = new LinkedHashSet<Object>();
        stronglyReachable = new LinkedHashSet<Object>();

        try
        {
            collectAllReachableObjects(object, stronglyReachable, "");
        }
        catch (IllegalArgumentException e)
        {
            e.printStackTrace();
        }
        catch (IllegalAccessException e)
        {
            e.printStackTrace();
        }
        softlyReachable.removeAll(weaklyReachable);
        softlyReachable.removeAll(phantomReachable);
        softlyReachable.removeAll(stronglyReachable);

        weaklyReachable.removeAll(softlyReachable);
        weaklyReachable.removeAll(phantomReachable);
        weaklyReachable.removeAll(stronglyReachable);

        phantomReachable.removeAll(softlyReachable);
        phantomReachable.removeAll(weaklyReachable);
        phantomReachable.removeAll(stronglyReachable);
    }

    private void collectAllReachableObjects(
        Object from, Set<Object> result, String indent) 
        throws IllegalArgumentException, IllegalAccessException
    {
        if (result.contains(from))
        {
            return;
        }
        result.add(from);
        Class<?> c = from.getClass();
        Class<?> leafClass = c;
        while (c != null)
        {
            //System.out.println(indent+"Class "+c);

            Field fields[] = c.getDeclaredFields();
            for (Field field : fields)
            {
                //System.out.println(indent+"Field "+field+" of "+c);

                if (Modifier.isStatic(field.getModifiers()))
                {
                    continue;
                }

                boolean wasAccessible = field.isAccessible();
                field.setAccessible(true);
                Object value = field.get(from);
                if (value != null)
                {
                    Set<Object> nextResult = stronglyReachable;
                    if (field.equals(REFERENCE_REFERENT_FIELD))
                    {
                        if (leafClass.equals(SoftReference.class))
                        {
                            nextResult = softlyReachable;
                        }
                        else if (leafClass.equals(WeakReference.class))
                        {
                            nextResult = weaklyReachable;
                        }
                        else if (leafClass.equals(PhantomReference.class))
                        {
                            nextResult = phantomReachable;
                        }
                    }
                    collectAllReachableObjects(value, nextResult, indent+"  ");
                }
                field.setAccessible(wasAccessible);
            }
            c = c.getSuperclass();
        }
    }

    List<Object> getSoftlyReachable()
    {
        return new ArrayList<Object>(softlyReachable);
    }
    List<Object> getWeaklyReachable()
    {
        return new ArrayList<Object>(weaklyReachable);
    }
    List<Object> getPhantomReachable()
    {
        return new ArrayList<Object>(phantomReachable);
    }
    List<Object> getStronglyReachable()
    {
        return new ArrayList<Object>(stronglyReachable);
    }
}


class TestObject
{
    String name;
    SoftReference<TestObject> softReference;
    WeakReference<TestObject> weakReference;
    PhantomReference<TestObject> phantomReference;
    Object strongReference;

    TestObject(String name, 
        TestObject soft, TestObject weak, TestObject phantom, TestObject strong)
    {
        this.name = name;
        if (soft != null)
        {
            softReference = new SoftReference<TestObject>(soft);
        }
        if (weak != null)
        {
            weakReference = new WeakReference<TestObject>(weak);
        }
        if (phantom != null)
        {
            phantomReference = new PhantomReference<TestObject>(phantom, new ReferenceQueue<>());
        }
        strongReference = strong;
    }
    @Override
    public String toString()
    {
        return name;
    }
}
Marco13
  • 53,703
  • 9
  • 80
  • 159
  • Thank you very much for your answer! It helped, but I would like to ask one more question. Would it be possible to create four lists containing only soft references, only weak references, only phantom references and only strong references? That would be just perfect. Do you know how to modify your code to achieve it? – user3572544 Apr 25 '14 at 12:30
  • @user3572544 Yes, asking a question is an *art* (rolling eyes). You mentioned that you are "struggling with the garbage collector". Whatever you mean by that: You should **NOT** use such a program to detect memory leaks or so. Use a profiler for that. A profiler performs an instrumentation via an agent, and provides *reliable* information. For the case of references: You never know whether or why a certain "referent" is `null`, so the information provided by such a utility class can only be a **very** questionable "snapshot" of a certain program state. – Marco13 Apr 25 '14 at 14:18
1

Unfortunately that doesn't really work. OK, I just want to have such methods:

   public static List < Object > getStronglyReachable (Object from)
     // contains all objects that are ONLY strongly reachable

   public static List < Object > getSoftlyReachable (Object from)
     // contains all objects that are strongly OR softly reachable

   public static List < Object > getWeaklyReachable (Object from)
     // contains all objects that are strongly OR softly OR weakly reachable

Remember that an object can be an array. The code needs something like:

// if an object is an array, iterate over its elements
if (from.getClass ().isArray ())
    for (int i = 0; i < Array.getLength (from); i++)
        collectAllReachableObjects (Array.get (from, i), result);
user3572544
  • 189
  • 7
  • You are right about the arrays. I came to the same conclusion. However the array looping code will need a null check, calling collectAllReachableObjects will a null will crash it. – Matthias Jul 02 '14 at 13:52