3

I'm trying to find a way to list which objects a run-time object is referring to. I know there is a way to enquire the jvm using oql but what I'd like to do is to query it from inside a program. Is there any API I could use?

T.J. Crowder
  • 1,031,962
  • 187
  • 1,923
  • 1,875
uzilan
  • 2,554
  • 2
  • 31
  • 46
  • If this is not a functional requirement, you can use a profiler or debugger. I think jhat/jstat helps dump the snapshot, so if you want to do some offline introspection it would be a easier. If it is a functional requirement, then it is an interesting one! – questzen Dec 06 '11 at 11:04

2 Answers2

3

You can do it via Reflection (java.lang.reflect).

How is described in this article. Basically, given this class that has private members:

public class Secret {

    private String secretCode = "It's a secret";

    private String getSecretCode(){
        return secretCode;     
    }
}

With Reflection, you can access all of its members (including the private ones), including their values. And so you look at all of its data members to see what they refer to (and of course, you can repeat the process if they also refer to other objects). Here's how to access their members (this code shows methods as well, which you probably won't need if you're just interested in data, but I didn't see any good reason to pull that part out):

import java.lang.reflect.Field; 
import java.lang.reflect.Method; 
import java.lang.reflect.InvocationTargetException; 

public class Hacker {

    private static final Object[] EMPTY = {};

    public void reflect(Object instance)
    throws IllegalAccessException, IllegalArgumentException, InvocationTargetException {
        Class secretClass = instance.getClass();

        // Print all the method names & execution result
        Method methods[] = secretClass.getDeclaredMethods(); 
        System.out.println("Access all the methods"); 
        for (int i = 0; i < methods.length; i++) { 
            System.out.println("Method Name: " + methods[i].getName());
            System.out.println("Return type: " + methods[i].getReturnType());
            methods[i].setAccessible(true);
            System.out.println(methods[i].invoke(instance, EMPTY) + "\n");
        }

        //  Print all the field names & values
        Field fields[] = secretClass.getDeclaredFields();
        System.out.println("Access all the fields");
        for (int i = 0; i < fields.length; i++){ 
            System.out.println("Field Name: " + fields[i].getName()); 
            fields[i].setAccessible(true); 
            System.out.println(fields[i].get(instance) + "\n"); 
        }
    }

    public static void main(String[] args){

        Hacker newHacker = new Hacker();

        try { 
            newHacker.reflect(new Secret());
        }
        catch (Exception e) {
            e.printStackTrace();
        }
    }
}

I've fixed a bug in their original code and made a small change to make it more clear that Hacker is not in any way tied to Secret (other than in main).

Update: Re your question below about the fields from base classes, here's an updated Hacker that does that (I've assumed you don't want to try to enumerate the fields on Object, so I've stopped there):

import java.lang.reflect.Field; 
import java.lang.reflect.Method; 
import java.lang.reflect.InvocationTargetException; 

public class Hacker {

    private static final Object[] EMPTY = {};

    public void reflect(Object instance)
    throws IllegalAccessException, IllegalArgumentException, InvocationTargetException {
        Class cls = instance.getClass();

        while (cls != null && cls != Object.class) {
            System.out.println("From class: " + cls.getName());

            // Print all the method names & execution result
            Method methods[] = cls.getDeclaredMethods(); 
            System.out.println("Access all the methods"); 
            for (int i = 0; i < methods.length; i++) { 
                System.out.println("Method Name: " + methods[i].getName());
                System.out.println("Return type: " + methods[i].getReturnType());
                methods[i].setAccessible(true);
                System.out.println(methods[i].invoke(instance, EMPTY) + "\n");
            }

            //  Print all the field names & values
            Field fields[] = cls.getDeclaredFields();
            System.out.println("Access all the fields");
            for (int i = 0; i < fields.length; i++){ 
                System.out.println("Field Name: " + fields[i].getName()); 
                fields[i].setAccessible(true); 
                System.out.println(fields[i].get(instance) + "\n"); 
            }

            // Go to the base class
            cls = cls.getSuperclass();
        }
    }

    public static void main(String[] args){

        Hacker newHacker = new Hacker();

        try { 
            newHacker.reflect(new Secret());
        }
        catch (Exception e) {
            e.printStackTrace();
        }
    }
}

When combined with

public class BaseSecret {

  private String baseSecretCode = "It's a base secret";

}

and

public class Secret extends BaseSecret {

    private String secretCode = "It's a secret";

    private String getSecretCode(){
        return secretCode;     
    }
}

you get:

$ java Hacker 
From class: Secret
Access all the methods
Method Name: getSecretCode
Return type: class java.lang.String
It's a secret

Access all the fields
Field Name: secretCode
It's a secret

From class: BaseSecret
Access all the methods
Access all the fields
Field Name: baseSecretCode
It's a base secret
T.J. Crowder
  • 1,031,962
  • 187
  • 1,923
  • 1,875
  • Thanks a lot. The API says that getDeclaredFields() excludes inherited methods. Do you know how to find these as well? – uzilan Dec 06 '11 at 11:42
  • @uzilan: Use [`getSuperclass`](http://docs.oracle.com/javase/6/docs/api/java/lang/Class.html#getSuperclass()) then rinse/repeat. I've added an example. – T.J. Crowder Dec 06 '11 at 11:53
  • Thanks again. Clearly I have to practis my API reading skills :) – uzilan Dec 06 '11 at 12:11
  • But won't this display the direct reference, that means if a method locally create an object and then it is referring to its method, it wont be possible to track that, will it be? – Naved Dec 06 '11 at 13:26
  • @Naved: The only way for the method to retain that reference after the method has returned is to store it as a field on the instance, which we can see via reflection. So that case would only come up if the method hadn't returned yet, like `main` or `Runnable#run`. So, yes, that can happen -- but it's more of an edge case. – T.J. Crowder Dec 06 '11 at 14:44
-1

You can use getClass() method of Object class to get the runtime class of an object.