有没有办法获得参考地址?
在Java中,有没有办法获得引用地址,比如说
String s = "hello"
我可以得到s本身的地址,我也可以得到引用的对象的地址吗?
您可以使用不安全的对象索引。 取决于JVM如何使用内存(32位地址,32位索引,带偏移量的32位索引,64位地址)会影响对象索引的有用程度。
这是一个程序,假定你在64位JVM中有32位索引。
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); } } }
运行在Java 6 update 26(带有压缩oops的64位)和Java 7.注意:地址和相对地址是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
或者有时候
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
你不能。 即使使用Java本地接口(JNI),也只能获得数据结构的不透明句柄,而不是指向真正的JVM对象的指针。
你为什么要这样的事情? 无论如何,即使使用本地代码,它也不一定会以任何forms使用。
你不能。 特别是,如果垃圾回收期间需要内存压缩,Java可以(和AFAIK)在堆中移动对象。
为什么你甚至需要或想要这个?
是的,你可以用不安全的方式来做,但不是那么直接。 把对象或实例引用放入一个int [],没关系。 长[]应该也是好的。
@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; }
其实地址可以用sun.misc.Unsafe获得,但是确实非常不安全。 GC经常移动物体。
在Java中不可能得到一个对象的引用地址,比如你的String。 在Java中,对象的引用地址对用户是隐藏的。
在C中,你可以通过指针的概念来做到这一点。 Java有一个类似的概念,在低层,这是参考地址。 引用就像一个C指针,但不是显式的。 在C中,可以通过*
来执行指针的引用操作,但在Java中,这是不可能的。
我不太喜欢C语言,也因为据我所知,指针并不是一个简单的概念来pipe理。 这是我喜欢Java的原因之一,因为程序员不需要担心对象的指针。
就像@jarnbjo所说,你可以检查,如果有些引用是相似的,像这样的语法:
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));
请注意, ==
检查引用地址的相等性。 如果要检查string值的相等性,请使用equals()
方法。
我build议你阅读这个问题, 这个维基百科页面和这个页面。