Is there a way to get a reference address?

44,039

Solution 1

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

Solution 2

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

Solution 3

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.

Solution 4

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

Solution 5

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.

Share:
44,039
Adam Lee
Author by

Adam Lee

Updated on April 12, 2020

Comments

  • Adam Lee
    Adam Lee about 4 years

    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?

  • Adam Lee
    Adam Lee over 12 years
    Just want to make sure some reference are the same.
  • jarnbjo
    jarnbjo over 12 years
    If you want to compare two references, you can do that with ==. There's no need for you to get the object's address.
  • Chris Aldrich
    Chris Aldrich over 12 years
    ++ good job. Was going to point this out from another SO link: stackoverflow.com/questions/5574241/…, but you showed how to do it with code so even better.
  • Chris Aldrich
    Chris Aldrich over 12 years
    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.
  • Ernest Friedman-Hill
    Ernest Friedman-Hill over 12 years
    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.
  • dawnstar
    dawnstar about 11 years
    @PeterLawrey Is there any detailed explanation about the 32-bit and 64-bit thing? I am pretty interested in that.
  • Vishy
    Vishy about 11 years
    @dawnstar Here is a good link on that wikis.oracle.com/display/HotSpotInternals/CompressedOops
  • Koray Tugay
    Koray Tugay over 9 years
    What are the packages of these classes?
  • qinxian
    qinxian over 7 years
    sun.misc.Unsafe. hg openjdk.java.net. jdk\src\share\classes\sun\misc\Unsafe.java