2012-01-11 3 views
29

In Java, c'è un modo per ottenere l'indirizzo di riferimento, direC'è un modo per ottenere un indirizzo di riferimento?

String s = "hello" 

posso ottenere l'indirizzo di s stessa, anche, posso ottenere l'indirizzo dell'oggetto che riferimento riguarda?

+0

specificare cosa intendi con indirizzo? Indirizzo in memoria? Garbage collector-id? – yankee

+0

Perché ne hai bisogno? – jarnbjo

+0

o indirizzo nella memoria o garbage collector id –

risposta

7

No, non è possibile. Anche utilizzando JNI (Java Native Interface), è possibile ottenere un handle opaco solo per la struttura dati, non un puntatore al vero oggetto JVM.

Perché vuoi una cosa del genere? Non sarebbe necessariamente in una forma che potresti usare per qualsiasi cosa, comunque, anche dal codice nativo.

+0

Voglio solo assicurarmi che alcuni riferimenti siano gli stessi. –

+11

Se vuoi confrontare due riferimenti, puoi farlo con ==. Non è necessario per te ottenere l'indirizzo dell'oggetto. – jarnbjo

4

No, non è possibile. Soprattutto non come Java può (e AFAIK lo fa) spostare gli oggetti nell'heap se è necessario per la compattazione della memoria durante la garbage collection.

Perché ne hai bisogno o lo vuoi?

2

In realtà l'indirizzo può essere ottenuto con sun.misc.Unsafe ma è davvero molto pericoloso. GC spesso sposta oggetti.

+1

Il link che ho postato nel mio commento di Peter Lawry mostra come si può fare, in modo tale che è possibile mantenere gli indirizzi corretti (perché se si creano oggetti utilizzando l'allocazione della memoria, probabilmente si può tecnicamente ottenere al di fuori del mucchio, dove GC non influirà tu). Immagino che sia così che funziona BigMemory di Terracotta. –

31

È possibile ottenere l'indice oggetto con Non sicuro. A seconda di come la JVM sta utilizzando la memoria (indirizzi a 32 bit, indice a 32 bit, indice a 32 bit con offset, indirizzo a 64 bit) può influire sull'utilità dell'indice dell'oggetto.

Ecco un programma che presume che tu abbia un indice a 32 bit in una JVM a 64 bit.

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

esecuzione su Java 6 aggiornamento 26 (64 bit con compresso oops) e Java 7. Nota: indirizzi e relativi indirizzi sono esadecimali.

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 

oa volte

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 
+2

++ buon lavoro. Stavo per farlo notare da un altro link SO: http://stackoverflow.com/questions/5574241/interesting-uses-of-sun-misc-unsafe, ma hai mostrato come farlo con il codice, quindi ancora meglio. –

+2

Questa è roba buona, ma i lettori dovrebbero aver cura di capire che questo non è portatile; la classe 'Unsafe' è un dettaglio di implementazione non documentato di JVM derivate da Sun. Questo codice non funziona su J9, JRockit, Dalvik, ecc. –

+0

@PeterLawrey Esiste qualche spiegazione dettagliata sulla cosa a 32 e 64 bit? Sono abbastanza interessato a questo. – dawnstar

1

non è possibile in Java per ottenere un indirizzo di riferimento di un oggetto, come la vostra String. L'indirizzo di riferimento di un oggetto è nascosto all'utente, in Java.

In C, puoi farlo attraverso il concetto di puntatori. Java ha un concetto simile, a basso livello, e questo è l'indirizzo di riferimento. Il riferimento è come un puntatore C, ma non è esplicito. In C, puoi fare l'operazione di referenziamento dei puntatori, tramite lo *, ma in Java, non è possibile.

Non mi piace molto il linguaggio C, anche perché i puntatori, secondo me, non sono un concetto facile da gestire. Questo è uno dei motivi per cui mi piace Java, perché il programmatore non ha bisogno di preoccuparsi del puntatore di un oggetto.

Come @jarnbjo dice, è possibile verificare, se alcuni riferimenti sono simili, con una sintassi simile a questo:

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

notare che == controlli l'uguaglianza di indirizzo di riferimento. Se si desidera verificare l'uguaglianza del valore delle stringhe, utilizzare il metodo equals().

io suggerisco di leggere this domanda SO, this pagina di Wikipedia e this pagina.

3

Sì, è possibile farlo con Unsafe, Althrough non è così direttamente. Inserisci l'oggetto o il riferimento di istanza in un int [], va bene. long [] dovrebbe andare bene pure.

@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; 
} 
+1

Quali sono i pacchetti di queste classi? –

+0

sun.misc.Unsafe. hg openjdk.java.net. jdk \ src \ share \ Classes \ sole \ Misc \ Unsafe.java – qinxian