2008-10-23 9 views
48

Słyszałem mieszane opinie na temat ilości pamięci, którą bajt zajmuje w programie java.Rozmiar bajta w pamięci - Java

Jestem świadomy, że można przechowywać nie więcej niż +127 w bajcie Java, a documentation mówi, że bajt ma tylko 8 bitów, ale here Powiedziano mi, że faktycznie zajmuje to samo ilości pamięci co int, i dlatego jest typem, który pomaga w zrozumieniu kodu, a nie wydajności.

Czy ktoś może to wyjaśnić i czy jest to problem związany z konkretną implementacją?

+0

jeden bajt zajmuje 4/8 bajtów w zależności od architektury procesora, bajt w bajcie [] ma dokładnie jeden bajt + nagłówek obiektu (+ wyrównanie wyrównania) – bestsss

+1

"* Jestem świadomy, że możesz przechowywać nie więcej niż +127 w bajcie Java *" - Nieprawda, w pewnym sensie. Możesz zapisać 256 różnych wartości w bajcie, dlatego ** możesz ** przechowywać w nim więcej niż 127: do 255, jeśli zaczynasz od 0. Wszystko zależy od tego, jak sobie z nimi poradzisz. Tylko ze względu na pedanterię: P –

Odpowiedz

59

Ok, nie było wiele dyskusji, a nie dużo kodu :)

Oto krótki odniesienia.Ma to normalne zastrzeżenia, jeśli chodzi o tego typu rzeczy - testowanie pamięci ma dziwne cechy związane z JITtingiem itp., Ale z odpowiednimi liczbami jest to przydatne. Ma dwa typy, z których każdy ma 80 członków - LotsOfBytes ma 80 bajtów, LotsOfInts ma 80 stron. Budujemy wiele z nich, upewnij się, że nie jesteś GC'd i sprawdzić zużycie pamięci:

class LotsOfBytes 
{ 
    byte a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, aa, ab, ac, ad, ae, af; 
    byte b0, b1, b2, b3, b4, b5, b6, b7, b8, b9, ba, bb, bc, bd, be, bf; 
    byte c0, c1, c2, c3, c4, c5, c6, c7, c8, c9, ca, cb, cc, cd, ce, cf; 
    byte d0, d1, d2, d3, d4, d5, d6, d7, d8, d9, da, db, dc, dd, de, df; 
    byte e0, e1, e2, e3, e4, e5, e6, e7, e8, e9, ea, eb, ec, ed, ee, ef; 
} 

class LotsOfInts 
{ 
    int a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, aa, ab, ac, ad, ae, af; 
    int b0, b1, b2, b3, b4, b5, b6, b7, b8, b9, ba, bb, bc, bd, be, bf; 
    int c0, c1, c2, c3, c4, c5, c6, c7, c8, c9, ca, cb, cc, cd, ce, cf; 
    int d0, d1, d2, d3, d4, d5, d6, d7, d8, d9, da, db, dc, dd, de, df; 
    int e0, e1, e2, e3, e4, e5, e6, e7, e8, e9, ea, eb, ec, ed, ee, ef; 
} 


public class Test 
{ 
    private static final int SIZE = 1000000; 

    public static void main(String[] args) throws Exception 
    {   
     LotsOfBytes[] first = new LotsOfBytes[SIZE]; 
     LotsOfInts[] second = new LotsOfInts[SIZE]; 

     System.gc(); 
     long startMem = getMemory(); 

     for (int i=0; i < SIZE; i++) 
     { 
      first[i] = new LotsOfBytes(); 
     } 

     System.gc(); 
     long endMem = getMemory(); 

     System.out.println ("Size for LotsOfBytes: " + (endMem-startMem)); 
     System.out.println ("Average size: " + ((endMem-startMem)/((double)SIZE))); 

     System.gc(); 
     startMem = getMemory(); 
     for (int i=0; i < SIZE; i++) 
     { 
      second[i] = new LotsOfInts(); 
     } 
     System.gc(); 
     endMem = getMemory(); 

     System.out.println ("Size for LotsOfInts: " + (endMem-startMem)); 
     System.out.println ("Average size: " + ((endMem-startMem)/((double)SIZE))); 

     // Make sure nothing gets collected 
     long total = 0; 
     for (int i=0; i < SIZE; i++) 
     { 
      total += first[i].a0 + second[i].a0; 
     } 
     System.out.println(total); 
    } 

    private static long getMemory() 
    { 
     Runtime runtime = Runtime.getRuntime(); 
     return runtime.totalMemory() - runtime.freeMemory(); 
    } 
} 

Wyjście na moim polu:

Size for LotsOfBytes: 88811688 
Average size: 88.811688 
Size for LotsOfInts: 327076360 
Average size: 327.07636 
0 

Tak oczywiście istnieje jakiś górny - 8 bajtów przez wygląda na to, chociaż jakoś tylko 7 dla LotsOfInts (? jak powiedziałem, są tu osobliwości) - ale chodzi o to, że pola bajtowe wydają się być spakowane dla LotsOfBytes tak, że zabierają (po usunięciu z góry) tylko jedną czwartą jako dużo pamięci, jak LotsOfInts.

+2

to zależy od JVM. Słońce wyrównuje się do granic 8 bajtów – kohlerm

+2

@kohlerm: To było z maszyną JVM Sun. –

+0

@ JonSkeet bardzo sprytny – Zo72

7

Java nie jest nigdy implementacją lub specyficzną dla platformy (dotyczy to co najmniej aż do primitive type sizes). Prymitywne typy mają zawsze gwarancję, że będą takie same, bez względu na to, na jakiej platformie się znajdujesz. Różni się od (i został uznany za ulepszenie) w C i C++, gdzie niektóre z pierwotnych typów były specyficzne dla platformy.

Ponieważ system operacyjny odpowiada za szybsze adresowanie czterech (lub ośmiu w systemie 64-bitowym) bajtów, JVM może przydzielić więcej bajtów do przechowywania bajtów pierwotnych, ale nadal można przechowywać tylko wartości. od -128 do 127 w nim.

+1

Nawet jeśli do przechowywania bajtu używa 4 bajtów, prawdopodobnie zostałaby spakowana tablica bajtów. Byłbym zaskoczony, gdyby bajt [4] używał 16 bajtów zamiast 4 bajtów. – Kip

+1

Prawdopodobnie. To * będzie * zależne od implementacji. Szczerze mówiąc nie wiem, która metoda byłaby szybsza. –

+1

artykuł jest poprawny, ale komentarz jest błędny. zmienna jednobajtowa zużywa 1 bajt + aligment. 8 zmiennych liczby bajtów na maszynie wirtualnej JVM firmy Sun kosztuje na przykład 8 bajtów – kohlerm

2

To, co wam powiedziano, jest dokładnie w porządku. Specyfikacja kodu Java byte ma tylko typy 4-bajtowe i 8-bajtowe.

bajt, char, int, short, boolean, float są przechowywane w 4 bajtach każdy.

podwójne i długie są przechowywane w 8 bajtach.

Jednak kod bajtu to tylko połowa historii. Istnieje również JVM, która jest specyficzna dla implementacji. Jest wystarczająco dużo informacji w kodzie bajtowym Java, aby określić, że zmienna została zadeklarowana jako bajt. Implementator JVM może zdecydować się na użycie tylko bajtu, chociaż myślę, że jest to wysoce nieprawdopodobne.

+0

Hmm ... to wydaje się być sprzeczne z http://java.sun.com/docs/books/jvms/second_edition/html/Overview.doc.html#31446: "Wartości integralnych typów wirtualnej maszyny Java są takie same jak w integralnych typach języka programowania Java (§2.4.1)" (Szukam teraz kodu bajtowego) –

+0

Masz coś: http://java.sun.com/docs/books/jvms/second_edition/html/Overview.doc.html#7565 - bipush, baload i bastore wydają się działać na typie bajtów ... * arytmetyczna * jest wykonywana tylko w int/longs , ale to inna sprawa. –

+0

Jon, opisałem specyfikację kodu bajtowego. To różni się od specyfikacji JVM. –

3

To zależy od tego, w jaki sposób JVM stosuje dopełnienie itp. Tablica bajtów będzie (w każdym zdrowym systemie) zapakowana w 1-bajt na element, ale klasa z czterema bajtowymi polami może być ciasno spakowana lub wyściełana na granice słów - zależne od implementacji.

+0

Czy to oznacza, że ​​użycie samego bajtu nie pozwoli na zaoszczędzenie pamięci, ale gdybym użył więcej niż jednej bajty zmiennej (lub tablicy bajtów), mógłbym zaoszczędzić znaczną pamięć. (Tj. Bajt [10] [10] może/powinien zająć mniej pamięci niż int [10] [10]) –

+0

Potencjalnie :) (Z pewnością spodziewałbym się, że tablica bajtów zajmie mniej miejsca niż int tablica - ale cztery bajty zmiennych vs cztery int zmiennych? Nie wiem.) –

+0

(Zobacz moją drugą odpowiedź dla dowodów, że przynajmniej niektóre JVM zrobić pakowanie.) –

2

Zawsze możesz używać długich i spakować dane w sobie, aby zwiększyć wydajność. Wtedy możesz zawsze gaurentee, że będziesz używał wszystkich 4 bajtów.

+0

lub nawet wszystkie 8 bajtów, w długim :) – JeeBee

+1

jeśli ty "Właściwie biorąc pod uwagę tego rodzaju zarządzanie pamięcią, myślę, że prawdopodobnie powinieneś używać C++ lub jakiegoś innego języka, który pozwala ci samodzielnie zarządzać pamięcią. Stracisz dużo więcej w obciążeniu JVM, niż zaoszczędzisz dzięki takim sztuczkom w Javie. – rmeador

+0

Ah. W C/C++ na systemach 32-bitowych int i long są 32-bitowe lub 4-bajtowe; Zapominam, że długo jest długa na innych systemach - zawsze mnie rozbawiałem, gdy dodali "longlong", aby wskazać 8-bajtowy ... no cóż. –

5

Ujawniające ćwiczenie polega na uruchomieniu javap na kodzie, który wykonuje proste operacje na bajtach i intach. Zobaczysz kody bajtowe, które oczekują parametrów int operujących na bajtach i wstawianych bajtów do współistnienia między nimi.

Należy zauważyć, że tablice bajtów nie są przechowywane jako tablice o wartościach 4-bajtowych, więc tablica o długości 1024 będzie korzystała z 1k pamięci (ignorując jakiekolwiek koszty ogólne).

14

Tak, zmienna bajtowa jest w rzeczywistości 4 bajtami w pamięci. Jednak nie dotyczy to tablic. Tablica bajtów składająca się z 20 bajtów jest w rzeczywistości tylko 20 bajtami w pamięci. Dzieje się tak dlatego, że Java Bytecode Language zna tylko ints i longs jako typy liczb (musi więc obsłużyć wszystkie liczby jako oba typy, 4 bajty lub 8 bajtów), ale zna tablice z każdym możliwym rozmiarem (tak, że są krótkie tablice fakt, że dwa bajty na wpis i tablice bajtowe są w rzeczywistości jednym bajtem na wpis).

+0

o tak, zapomniałem tego niezbyt szczegółowego! –

+1

Nie zapominaj, że tablica bajtów ma również normalne koszty ogólne związane z byciem obiektem i długość. Aha, a twoją zmienną jest referencja (4 lub 8 bajtów). Tak więc, aby faktycznie mieć 20 bajtów dostępnych i użytecznych będzie wymagać 36 bajtów, zakładając brak aliasingu. Trzymałbym się 20 bajtowych pól :) –

+0

@ Jon @ Mecki Czy możesz podać mniej lub bardziej dokładną formułę do obliczenia wielkości tablicy 'int []? Czy będzie to "4 [= długość] + 4 [= int_size] * długość (tablica) + 8_byte_align"? –

0

zobaczyć moje MonitoringTools na mojej stronie (www.csd.uoc.gr/~andreou)

 
class X { 
    byte b1, b2, b3...; 
} 

long memoryUsed = MemoryMeasurer.measure(new X()); 

(Może być stosowany dłużej złożone wykresy obiektów/obiektów)

W 1.6 JDK Sun wydaje się, że bajt rzeczywiście zajmuje jeden bajt (w starszych wersjach, int ~ byte pod względem pamięci). Należy jednak zauważyć, że nawet w starszych wersjach bajt [] był również zapakowany do jednego bajtu na wpis.

W każdym razie chodzi o to, że nie ma potrzeby wykonywania złożonych testów, takich jak powyższy Jon Skeet, które podają jedynie szacunki. Możemy bezpośrednio zmierzyć rozmiar obiektu!

+0

Twój link jest zepsuty – Pacerier

+0

Podejrzewam, że teraz żyje [tutaj] (http://code.google.com/p/memory-measurer/) – magiconair

0

Czytając powyższych uwag wydaje się, że mój wniosek będzie zaskoczeniem dla wielu (jest również zaskoczeniem dla mnie), więc jest warty powtórzenia:

  • Stara wielkość (int) == rozmiar (bajt) dla zmiennych posiada nie więcej, przynajmniej w języku Java firmy Sun 6.

Zamiast rozmiar (bajt) == 1 bajt (!!)

-3

wydaje się, że odpowiedź jest może zależeć od wersji JVM i prob a także architekturę procesora, na której pracujesz. Linia procesorów Intela skutecznie przetwarza bajty (dzięki 8-bitowej historii procesorów). Niektóre układy RISC wymagają wyrównania słów (4 bajty) dla wielu operacji. Alokacja pamięci może być różna dla zmiennych na stosie, pól w klasie i w tablicy.

0

Chciałem tylko zwrócić uwagę, że oświadczenie

można przechowywać nie więcej niż +127 w bajcie java

nie jest rzeczywiście poprawna.

Zawsze można zapisać 256 różnych wartości w bajcie, dlatego łatwo można ustawić zakres 0..255 tak, jakby był bajtem "bez znaku".

Wszystko zależy od tego, jak obchodzić się z tymi 8 bitami.

Przykład:

byte B=(byte)200;//B contains 200 
System.out.println((B+256)%256);//Prints 200 
System.out.println(B&0xFF);//Prints 200 
4

Zrobiłem test używając http://code.google.com/p/memory-measurer/ Zauważ, że używam 64-bitowej Oracle/Sun Java 6, bez kompresji odniesień itd

Każdy obiekt zajmuje trochę miejsca , plus JVM musi znać adres tego obiektu, a "adres" sam w sobie to 8 bajtów.

Z prymitywów, wygląda prymitywy są odlewane do 64-bitowej dla lepszej wydajności (oczywiście!):

byte: 16 bytes, 
int: 16 bytes, 
long: 24 bytes. 

z tablicami:

byte[1]: 24 bytes 
int[1]: 24 bytes 
long[1]: 24 bytes 

byte[2]: 24 bytes 
int[2]: 24 bytes 
long[2]: 32 bytes 

byte[4]: 24 bytes 
int[4]: 32 bytes 
long[4]: 48 bytes 

byte[8]: 24 bytes => 8 bytes, "start" address, "end" address => 8 + 8 + 8 bytes 
int[8]: 48 bytes => 8 integers (4 bytes each), "start" address, "end" address => 8*4 + 8 + 8 bytes 
long[8]: 80 bytes => 8 longs (8 bytes each), "start" address, "end" address => 8x8 + 8 + 8 bytes 

A teraz zgadnij co ...

byte[8]: 24 bytes 
byte[1][8]: 48 bytes 
    byte[64]: 80 bytes 
byte[8][8]: 240 bytes 

PS Oracle Java 6, najnowszy i największy, 64-bitowy, 1.6.0_37, MacOS X

Powiązane problemy