user3572544
user3572544

Reputation: 189

Objects reachable by strong or weak references in Java

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)

Upvotes: 1

Views: 986

Answers (2)

user3572544
user3572544

Reputation: 189

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);

Upvotes: 1

Marco13
Marco13

Reputation: 54679

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;
    }
}

Upvotes: 1

Related Questions