40

In Java, is there a way to get reference address, say

String s = "hello"

can I get the address of s itself , also, can I get the address of the object which reference refers to?

Peter Lawrey
  • 525,659
  • 79
  • 751
  • 1,130
Adam Lee
  • 24,710
  • 51
  • 156
  • 236
  • please specify what do you mean with address? Address in memory? Garbage collector-id? – yankee Jan 11 '12 at 13:37
  • either address in memory or garbage collector id – Adam Lee Jan 11 '12 at 13:39
  • 14
    Either go back to the dark side of "C" where memory addresses plague your every thought, or come over to the light side, where memory addresses are neither needed nor wanted. Choose your path! – Bohemian Jan 11 '12 at 13:39
  • 2
    You really shouldn't want to do it, which is what most of the people below are saying. Peter Lawry shows that you can do it (he beat me to mentioning it). Others are "wrong" in that sense. However, there is a reason the class is called "Unsafe". Another reason, is that this is a Sun/Oracle class and does not have to be implemented by other JVM implementors, so you can't rely on it in your code if you don't know where you are running. – Chris Aldrich Jan 11 '12 at 13:56
  • A rare circumstance for using this: instrumenting an activex / ocx control from java where the activex control requires a `long` pointer to a byte array as a parameter to one of it's methods. In other words, if you need to "bridge" between java and another language such as C++ this might be useful (if there are absolutely no other options). – vikingsteve Nov 18 '13 at 18:51
  • There are some legitimate – ExMathGuy Jan 11 '17 at 14:25
  • 1
    There are some legitimate reasons to use this in debugging, like tracking memory leaks with logging of addresses together with heap dump. Also tracking performance issues involving cache, such as false sharing. – ExMathGuy Jan 11 '17 at 14:36

5 Answers5

45

You can get the object index with Unsafe. Depending on how the JVM is using the memory (32-bit addresses, 32-bit index, 32-bit index with offset, 64-bit address) can affect how useful the object index is.

Here is a program which assumes you have 32-bit index in a 64-bit JVM.

import sun.misc.Unsafe;

import java.lang.reflect.Field;
import java.util.Arrays;
import java.util.Collections;

public class OrderOfObjectsAfterGCMain {
    static final Unsafe unsafe = getUnsafe();
    static final boolean is64bit = true; // auto detect if possible.

    public static void main(String... args) {
        Double[] ascending = new Double[16];
        for(int i=0;i<ascending.length;i++)
            ascending[i] = (double) i;

        Double[] descending = new Double[16];
        for(int i=descending.length-1; i>=0; i--)
            descending[i] = (double) i;

        Double[] shuffled = new Double[16];
        for(int i=0;i<shuffled.length;i++)
            shuffled[i] = (double) i;
        Collections.shuffle(Arrays.asList(shuffled));

        System.out.println("Before GC");
        printAddresses("ascending", ascending);
        printAddresses("descending", descending);
        printAddresses("shuffled", shuffled);

        System.gc();
        System.out.println("\nAfter GC");
        printAddresses("ascending", ascending);
        printAddresses("descending", descending);
        printAddresses("shuffled", shuffled);

        System.gc();
        System.out.println("\nAfter GC 2");
        printAddresses("ascending", ascending);
        printAddresses("descending", descending);
        printAddresses("shuffled", shuffled);
    }

    public static void printAddresses(String label, Object... objects) {
        System.out.print(label + ": 0x");
        long last = 0;
        int offset = unsafe.arrayBaseOffset(objects.getClass());
        int scale = unsafe.arrayIndexScale(objects.getClass());
        switch (scale) {
            case 4:
                long factor = is64bit ? 8 : 1;
                final long i1 = (unsafe.getInt(objects, offset) & 0xFFFFFFFFL) * factor;
                System.out.print(Long.toHexString(i1));
                last = i1;
                for (int i = 1; i < objects.length; i++) {
                    final long i2 = (unsafe.getInt(objects, offset + i * 4) & 0xFFFFFFFFL) * factor;
                    if (i2 > last)
                        System.out.print(", +" + Long.toHexString(i2 - last));
                    else
                        System.out.print(", -" + Long.toHexString( last - i2));
                    last = i2;
                }
                break;
                case 8:
                    throw new AssertionError("Not supported");
        }
        System.out.println();
    }

    private static Unsafe getUnsafe() {
        try {
            Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe");
            theUnsafe.setAccessible(true);
            return (Unsafe) theUnsafe.get(null);
        } catch (Exception e) {
            throw new AssertionError(e);
        }
    }
}

Running on Java 6 update 26 (64-bit with compressed oops) and Java 7. Note: addresses and relative addresses are in hex.

Before GC
ascending: 0x782322b20, +18, +18, +18, +18, +18, +18, +18, +18, +18, +18, +18, +18, +18, +18, +18
descending: 0x782322e58, -18, -18, -18, -18, -18, -18, -18, -18, -18, -18, -18, -18, -18, -18, -18
shuffled: 0x782322ec0, +78, -30, +90, -c0, +18, +90, +a8, -30, -d8, +f0, -30, -90, +60, -48, +60

After GC
ascending: 0x686811590, -18, -18, -18, -18, -18, -18, -18, -18, -18, -18, -18, -18, -18, -18, -18
descending: 0x686811410, -18, -18, -18, -18, -18, -18, -18, -18, -18, -18, -18, -18, -18, -18, -18
shuffled: 0x686811290, -18, -18, -18, -18, -18, -18, -18, -18, -18, -18, -18, -18, -18, -18, -18

OR sometimes

Before GC
ascending: 0x782322b20, +18, +18, +18, +18, +18, +18, +18, +18, +18, +18, +18, +18, +18, +18, +18
descending: 0x782322e58, -18, -18, -18, -18, -18, -18, -18, -18, -18, -18, -18, -18, -18, -18, -18
shuffled: 0x782323028, -168, +150, -d8, -30, +60, +18, +30, +30, +18, -108, +30, -48, +78, +78, -30

After GC
ascending: 0x6868143c8, +4db0, +7120, -bd90, +bda8, -bd90, +4d40, +18, +18, -12710, +18, +80, +18, +ffa8, +220, +6b40
descending: 0x68681d968, +18, +d0, +e0, -165d0, +a8, +fea8, +c110, -5230, -d658, +6bd0, +be10, +1b8, +75e0, -19f68, +19f80
shuffled: 0x686823938, -129d8, +129f0, -17860, +4e88, +19fe8, -1ee58, +18, +18, +bb00, +6a78, -d648, -4e18, +4e40, +133e0, -c770
Peter Lawrey
  • 525,659
  • 79
  • 751
  • 1,130
  • 2
    ++ good job. Was going to point this out from another SO link: http://stackoverflow.com/questions/5574241/interesting-uses-of-sun-misc-unsafe, but you showed how to do it with code so even better. – Chris Aldrich Jan 11 '12 at 13:54
  • 2
    This is neat stuff, but readers should take care to realize that this is not portable; the `Unsafe` class is an undocumented implementation detail of Sun-derived JVMs. This code won't work on J9, JRockit, Dalvik, etc. – Ernest Friedman-Hill Jan 11 '12 at 14:32
  • @PeterLawrey Is there any detailed explanation about the 32-bit and 64-bit thing? I am pretty interested in that. – dawnstar May 11 '13 at 12:55
  • @dawnstar Here is a good link on that https://wikis.oracle.com/display/HotSpotInternals/CompressedOops – Peter Lawrey May 11 '13 at 12:59
6

Yes, You can do it with Unsafe, Althrough it's not so directly. Put the object or instance reference into a int[], that's ok. long[] should be fine as well.

    @Test
public void test1() throws Exception {
    Unsafe unsafe = Util.unsafe;
    int base = unsafe.arrayBaseOffset(int[].class);
    int scale = unsafe.arrayIndexScale(int[].class);
    int shift = 31 - Integer.numberOfLeadingZeros(scale);
    System.out.printf("base: %s, scale %s, shift: %s\n", base, scale, shift);
    base = unsafe.arrayBaseOffset(Object[].class);
    scale = unsafe.arrayIndexScale(Object[].class);
    shift = 31 - Integer.numberOfLeadingZeros(scale);
    System.out.printf("base: %s, scale %s, shift: %s\n", base, scale, shift);
    int[] ints = { 1, 2, 0 };
    String string = "abc";
    System.out.printf("string: id: %X, hash: %s\n", System.identityHashCode(string), string.hashCode());
    unsafe.putObject(ints, offset(2, shift, base), string);
    System.out.printf("ints: %s, %X\n", Arrays.toString(ints), ints[2]);
    Object o = unsafe.getObject(ints, offset(2, shift, base));
    System.out.printf("ints: %s\n", o);
    assertSame(string, o);

    Object[] oa = { 1, 2, string };
    o = unsafe.getObject(oa, offset(2, shift, base));
    assertSame(string, o);
    int id = unsafe.getInt(oa, offset(2, shift, base));
    System.out.printf("id=%X\n", id);
}

public static long offset(int index, int shift, int base) {
    return ((long) index << shift) + base;
}
qinxian
  • 367
  • 3
  • 6
5

No, you cannot. Even using the Java Native Interface (JNI), you can only get an opaque handle to the data structure, not a pointer to the real JVM object.

Why would you want such a thing? It wouldn't necessarily be in a form you could use for anything, anyway, even from native code.

Ernest Friedman-Hill
  • 80,601
  • 10
  • 150
  • 186
3

Actually address can be obtained with sun.misc.Unsafe but it is really very unsafe. GC often moves objects.

Vadzim
  • 24,954
  • 11
  • 143
  • 151
  • 1
    The link I posted in my comment to Peter Lawry shows how you can do it, such that you can retain correct addresses (cause if you create objects using memory allocation, you can probably technically get outside the heap, where GC won't affect you). I'm guessing that is how Terracotta's BigMemory works. – Chris Aldrich Jan 11 '12 at 13:58
2

It's not possible in Java to get a reference address of an object, like your String. The reference address of an object is hidden to the user, in Java.

In C, you can do this, through the concept of pointers. Java has a similar concept, at low-level,and this is the reference address. The reference is like a C pointer, but it's not explicit. In C, you can do the operation of referencing of pointers, through the *, but in Java, it's not possible.

I don't like very much the C language, also because the pointers, according to me, are not an easy concept to manage. This is one of the reasons I like Java, because the programmer doesn't need to worry about the pointer of an object.

Like @jarnbjo says, you can check, if some references are similar, with a syntax like this:

String s = "hello";
String g = s;
System.out.println("Are the reference addresses similar? "+(s==g));
g = "goodbye";
System.out.println("Are the reference addresses similar? "+(s==g));

Note that == checks the equality of reference address. If you want to check the equality of the value of the strings, use the equals() method.

I suggest you to read this SO question, this Wikipedia page and this page.

Community
  • 1
  • 1
Alberto Solano
  • 7,972
  • 3
  • 38
  • 61