2013-02-20 13 views
6

Kod u dołu tego pytania jest nieco długi, ale w zasadzie tworzy kilka obiektów i określa ich rozmiar w pamięci. I wykonanie kodu z następujących parametrów JVM (TLAB uniknąć alokacji pamięci kawałek i podobno uzyskać dokładne dane użycia pamięci):Dokładny pomiar wielkości obiektów

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

uruchomić kod na 64 bitowym Hotspot JVM i uzyskać następujące dane wyjściowe:

Java Internetu (TM) 64-bitowego serwera VM
obiekt 16 bajtów

obiektu z 1 int 16 bajtów
obiektu 2 wskazówki: 24 bajty
obiektu z 3 wskazówki: 24 bajtów

obiektu z 1 długi: 24 bajty
obiektu 2 długościami: 32 bajtów
obiektu z 3 długościami: 40 bajtów

obiektu z 1 odnośnik 16 bajtów
obiekt 2 publikacjach: 24 bajtów
przedmiot z 3 publikacjach: 24 bajtów

że wnioski:

  • Obiekt zajmuje 12 bajtów, wyrównanych do 16 bajtów.
  • int trwa 4 bajty (1 obiekt z jednym int 12 + 4 = jeszcze 16 bajtów, z 2 wskazówki: 12 + 8 = 20 dostosowane do 24 bajtów)
  • długo trwa 8 bajtów (1 przedmiotu z jednego długi jest 12 + 8 = 20 bajtów, wyrównane do 24 bajtów)

Ale ja usiłuję zrozumieć dlaczego odniesienia nie używać tak dużo miejsca jak long s.

Ponieważ referencje mają 8 bajtów na 64-bitowej maszynie JVM, oczywistym wnioskiem jest to, że metodologia pomiaru ma wadę *. Czy możesz wyjaśnić, co się dzieje i co można zrobić, aby to naprawić?

* Uwagi:
- brak biegnie GC podczas pomiaru.
- używanie profilera Netbeans daje podobne wyniki.

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

Odpowiedz

5

Ponieważ referencje są 8 bajtów na 64-bitowe JVM

jest to Twoja potencjalnie błędne założenie.

HotSpot jest w stanie wykorzystać "compressed oops" użyć wartości 32-bitowych dla odniesień w niektóre miejscach JVM (kopalnia nacisk):

Które oops są skompresowane?

W JVM trybu ILP32 lub jeśli flaga UseCompressedOops jest wyłączona w trybie LP64, wszystkie oops są rodzimymi słowami maszynowymi.

Jeśli UseCompressedOops prawda następujące ups w stosie będą ściskane:

  • pole klass każdego obiektu
  • każde pole przykład OOP
  • każdy element tablicy oop (objArray)

I podejrzany to właśnie dzieje się w twoim przypadku.

go przetestować za pomocą

-XX:-UseCompressedOops 

lub

-XX:+UseCompressedOops 

Na moim komputerze, domyślnie uzyskać takie same wyniki jak ty, ale z -XX:-UseCompressedOops widzę:

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 

... która prawdopodobnie jest bliższa temu, czego oczekiwałeś :)

+0

Aargh - Myślałem, że 'UseCompressedOops' domyślnie false - okazuje się, że jest włączona domyślnie. Dobrze zauważony. – assylias

+0

@assylias: Zobacz moją edycję, aby włączyć/wyłączyć. –