2013-02-20 14 views
6

Der Code am Ende dieser Frage ist ein bisschen lang, aber im Grunde erstellt ein paar Objekte und bestimmt ihre Größe im Speicher. Ich führen Sie den Code mit den folgenden JVM-Parameter (TLAB chunk Speicherzuordnung zu vermeiden und angeblich genaue Speichernutzung Zahlen erhalten):Genaue Messung der Objektgrößen

-server -Xms2000m -Xmx2000m -verbose:gc -XX:-UseTLAB 

ich den Code auf einem 64-Bit-Hotspot JVM ausgeführt werden und erhalten die folgende Ausgabe:

HotSpot Java (TM) 64-Bit-Server VM
Object: 16 Bytes

Object mit 1 int: 16 Bytes
Objekt mit 2 ints: 24 Bytes
Objekt mit 3 ints: 24 Bytes

Object mit 1 long: 24 Bytes
Objekt mit 2 longs: 32 Bytes
Objekt mit 3 longs: 40 Byte

Object mit 1 Referenz: 16 Bytes
Objekt mit 2 Referenzen: 24 Byte
Objekt mit 3 Referenzen: 24 Bytes

I schließen, dass:

  • ein Objekt dauert 12 Bytes, ausgerichtet auf 16 Bytes.
  • ein int dauert 4 Bytes (1 Objekt mit einem int ist 12 + 4 = noch 16 Bytes, mit 2 ints: 12 + 8 = 20 auf 24 Bytes ausgerichtet)
  • ein langer dauert 8 Bytes (1 Objekt mit einem lang ist 12 + 8 = 20 Bytes, ausgerichtet auf 24 Byte)

Aber ich kämpfen, um zu verstehen, warum Referenzen nicht so viel Platz wie long s verwenden.

Da Referenzen auf einer 64-Bit-JVM 8 Byte sind, ist die naheliegende Schlussfolgerung, dass die Messmethode einen Fehler aufweist *. Können Sie erklären, was vor sich geht und was zu tun ist, um es zu beheben?

* Anmerkungen:
- keine GC läuft während der Messung.
- die Verwendung des Netbeans-Profilers führt zu ähnlichen Ergebnissen.

public class TestMemoryReference { 

    private static final int SIZE = 100_000; 
    private static Runnable r; 
    private static Object o = new Object(); 
    private static Object o1 = new Object(); 
    private static Object o2 = new Object(); 
    private static Object o3 = new Object(); 

    public static class ObjectWith1Int { int i; } 
    public static class ObjectWith2Ints { int i, j; } 
    public static class ObjectWith3Ints { int i, j, k; } 
    public static class ObjectWith1Long { long i; } 
    public static class ObjectWith2Longs { long i, j; } 
    public static class ObjectWith3Longs { long i, j, k; } 
    public static class ObjectWith1Object { Object o = o1; } 
    public static class ObjectWith2Objects { Object o = o1; Object p = o2; } 
    public static class ObjectWith3Objects { Object o = o1; Object p = o2; Object q = o3; } 

    private static void test(Runnable r, String name, int numberOfObjects) { 
     long mem = Runtime.getRuntime().freeMemory(); 
     r.run(); 
     System.out.println(name + ":" + (mem - Runtime.getRuntime().freeMemory())/numberOfObjects + " bytes "); 
    } 

    public static void main(String[] args) throws Exception { 
     System.out.println(System.getProperty("java.vm.name") + " "); 

     r = new Runnable() { public void run() { for (int i = 0; i < SIZE; i++) o = new Object(); } }; 
     test(r, "Object", SIZE); 

     r = new Runnable() { public void run() { for (int i = 0; i < SIZE; i++) o = new ObjectWith1Int(); } }; 
     test(r, "Object with 1 int", SIZE); 

     r = new Runnable() { public void run() { for (int i = 0; i < SIZE; i++) o = new ObjectWith2Ints(); } }; 
     test(r, "Object with 2 ints", SIZE); 

     r = new Runnable() { public void run() { for (int i = 0; i < SIZE; i++) o = new ObjectWith3Ints(); } }; 
     test(r, "Object with 3 ints", SIZE); 

     r = new Runnable() { public void run() { for (int i = 0; i < SIZE; i++) o = new ObjectWith1Long(); } }; 
     test(r, "Object with 1 long", SIZE); 

     r = new Runnable() { public void run() { for (int i = 0; i < SIZE; i++) o = new ObjectWith2Longs(); } }; 
     test(r, "Object with 2 longs", SIZE); 

     r = new Runnable() { public void run() { for (int i = 0; i < SIZE; i++) o = new ObjectWith3Longs(); } }; 
     test(r, "Object with 3 longs", SIZE); 

     r = new Runnable() { public void run() { for (int i = 0; i < SIZE; i++) o = new ObjectWith1Object(); } }; 
     test(r, "Object with 1 reference", SIZE); 

     r = new Runnable() { public void run() { for (int i = 0; i < SIZE; i++) o = new ObjectWith2Objects(); } }; 
     test(r, "Object with 2 references", SIZE); 

     r = new Runnable() { public void run() { for (int i = 0; i < SIZE; i++) o = new ObjectWith3Objects(); } }; 
     test(r, "Object with 3 references", SIZE); 
    } 
} 

Antwort

5

Da Referenzen 8 Byte auf einem 64-Bit-JVM

potentiell fehlerhafte Annahme Dies wird Sie sind.

HotSpot ist in der Lage "compressed oops" zu verwenden 32-Bit-Werte für Referenzen zu verwenden, in einige Orte der JVM (Hervorhebung von mir):

Welche oops komprimiert werden?

In einer ILP32-Modus-JVM oder wenn das UseCompressedOops-Flag im LP64-Modus deaktiviert ist, sind alle Oops die systemeigene Maschinenwortgröße.

Wenn UseCompressedOops wahr ist, wird die folgenden oops in der Halde wird komprimiert werden:

  • die Klass Feld jeden Objekt
  • jedes oop Instanzfeld
  • jedes Element eines OOP-Array (objArray)

I Verdächtigen Dies ist, was in Ihrem Fall passiert.

Testen sie unter Verwendung

-XX:-UseCompressedOops 

oder

-XX:+UseCompressedOops 

Auf meinem Rechner standardmäßig ich die gleichen Ergebnisse wie man bekommt, aber mit -XX:-UseCompressedOops ich sehe:

Object:16 bytes 
Object with 1 int:24 bytes 
Object with 2 ints:24 bytes 
Object with 3 ints:32 bytes 
Object with 1 long:24 bytes 
Object with 2 longs:32 bytes 
Object with 3 longs:40 bytes 
Object with 1 reference:24 bytes 
Object with 2 references:32 bytes 
Object with 3 references:40 bytes 

... das ist wahrscheinlich näher an dem, was Sie erwartet haben :)

+0

Aargh - Ich dachte, das 'UseCompressedOops' zu falschen Verzug geraten - es stellt sich heraus, es ist standardmäßig aktiviert ist. Gut beobachtet. – assylias

+0

@assylias: Siehe meine Bearbeitung, wie man es ein-/ausschaltet. –