2008-10-23 41 views
48

Tôi đã nghe ý kiến ​​hỗn hợp về số lượng bộ nhớ mà một byte mất trong một chương trình java.Kích thước của một byte trong bộ nhớ - Java

Tôi biết bạn có thể lưu trữ không quá +127 trong một byte java và documentation nói rằng một byte chỉ là 8 bit nhưng here Tôi được biết rằng nó thực sự chiếm cùng một lượng bộ nhớ như một int, và do đó chỉ là một loại giúp hiểu mã và không hiệu quả.

Có ai có thể xóa điều này và đây có phải là vấn đề cụ thể về triển khai không?

+0

một byte đơn mất 4/8 byte tùy thuộc vào kiến ​​trúc cpu, byte trong byte [] lấy chính xác một byte + tiêu đề đối tượng (+ theo chiều dọc) – bestsss

+1

"* Tôi biết bạn có thể lưu trữ không quá +127 trong một byte java *" - Không đúng, theo nghĩa nào đó. Bạn có thể lưu trữ 256 giá trị khác nhau trong một byte, do đó bạn ** có thể ** lưu trữ cách hơn 127 trong nó: lên đến 255 nếu bạn bắt đầu từ 0. Tất cả phụ thuộc vào cách bạn xử lý những 8 bit đó. Chỉ vì lợi ích của người đi bộ: P –

Trả lời

59

OK, đã có rất nhiều cuộc thảo luận và không nhiều mã :)

Đây là điểm chuẩn nhanh.Nó có những lời khuyên bình thường khi nói đến loại điều này - kiểm tra bộ nhớ có những điều kỳ quặc do JITting vv, nhưng với số lượng lớn phù hợp nó có ích anyway. Nó có hai loại, mỗi loại có 80 thành viên - LotsOfBytes có 80 byte, LotsOfInts có 80 ints. Chúng tôi xây dựng rất nhiều trong số họ, chắc chắn họ không GC'd, và kiểm tra sử dụng bộ nhớ:

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

Output trên hộp của tôi:

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

Vì vậy, rõ ràng là có một số chi phí - 8 byte bởi ngoại hình của nó, mặc dù bằng cách nào đó chỉ có 7 cho LotsOfInts (như tôi đã nói, có những điều kỳ quặc ở đây) - nhưng điểm là các trường byte xuất hiện để được đóng gói trong LotsOfBytes như vậy mà phải mất (sau khi loại bỏ trên cao) chỉ một phần tư nhiều bộ nhớ như LotsOfInts.

+2

nó phụ thuộc vào JVM. Mặt trời căn chỉnh đến 8 ranh giới Byte – kohlerm

+2

@ kohlerm: Đó là với Sun JVM. –

+0

@JonSkeet rất thông minh – Zo72

7

Java không bao giờ được triển khai hoặc nền tảng cụ thể (ít nhất là đến mức primitive type sizes có liên quan). Các kiểu nguyên thủy luôn được đảm bảo giữ nguyên cho dù bạn đang sử dụng nền tảng nào. Điều này khác với (và được xem là cải tiến) C và C++, trong đó một số kiểu nguyên thủy là nền tảng cụ thể.

Vì hệ điều hành cơ bản nhanh hơn để xử lý bốn (hoặc tám, trong hệ thống 64 bit), JVM có thể phân bổ nhiều byte hơn để lưu trữ byte gốc, nhưng bạn vẫn có thể lưu trữ các giá trị từ -128 đến 127 trong đó.

+1

Thậm chí nếu nó sử dụng 4 byte để lưu trữ một byte, một mảng byte có thể sẽ được đóng gói. Tôi sẽ ngạc nhiên nếu một byte [4] sử dụng 16 byte thay vì 4 byte. – Kip

+1

Có thể. Điều đó * sẽ * được thực hiện cụ thể. Tôi thành thật không biết phương pháp nào sẽ nhanh hơn. –

+1

bài viết là chính xác nhưng nhận xét sai. một biến byte đơn tiêu thụ 1 byte + aligment. 8 Biến thiên byte trên Sun JVM ví dụ 8 byte – kohlerm

2

Điều bạn đã được cho là chính xác. Đặc tả mã byte Java chỉ có loại 4 byte và loại 8 byte.

byte, char, int, ngắn, boolean, float đều được lưu trữ trong 4 byte mỗi.

đôi và dài được lưu trữ trong 8 byte.

Tuy nhiên mã byte chỉ là một nửa câu chuyện. Ngoài ra còn có các JVM, được thực hiện cụ thể. Có đủ thông tin trong mã byte Java để xác định rằng một biến được khai báo là một byte. Một người triển khai JVM có thể quyết định chỉ sử dụng một byte, mặc dù tôi nghĩ điều đó rất khó xảy ra.

+0

Hmm ... có vẻ như đi ngược lại http://java.sun.com/docs/books/jvms/second_edition/html/Overview.doc.html#31446: "Các giá trị của các kiểu tích phân của máy ảo Java giống như các kiểu tích phân của ngôn ngữ lập trình Java (§2.4.1)" (Tìm kiếm các công cụ bytecode bây giờ ...) –

+0

Có điều gì đó: http://java.sun.com/docs/books/jvms/second_edition/html/Overview.doc.html#7565 - bipush, baload và bastore xuất hiện để hoạt động trên loại byte ... * số học * chỉ được thực hiện trên ints/longs , nhưng đó là một vấn đề khác. –

+0

Jon, tôi mô tả thông số mã byte. Điều này khác với thông số JVM. –

3

Phụ thuộc vào cách JVM áp dụng vùng đệm. Một mảng byte (trong bất kỳ hệ thống lành mạnh nào) được đóng gói thành 1 byte cho mỗi phần tử, nhưng một lớp có bốn trường byte có thể được đóng gói hoặc đệm chặt vào ranh giới từ - nó phụ thuộc vào việc triển khai thực hiện.

+0

Điều này có nghĩa rằng việc sử dụng một byte sẽ không tiết kiệm bộ nhớ, nhưng nếu tôi đã sử dụng nhiều hơn một biến byte (hoặc một mảng byte) tôi có thể lưu bộ nhớ đáng kể. (Tức là byte [10] [10] có thể/nên mất ít bộ nhớ hơn int [10] [10]) –

+0

Có khả năng :) (Chắc chắn tôi mong đợi một mảng byte chiếm ít không gian hơn int mảng - nhưng bốn biến byte vs bốn biến int? Không biết.) –

+0

(Xem câu trả lời khác của tôi cho bằng chứng rằng ít nhất một số JVM làm đóng gói.) –

2

Bạn luôn có thể sử dụng thời gian dài và tự đóng gói dữ liệu để tăng hiệu quả. Sau đó, bạn luôn có thể gaurentee bạn sẽ sử dụng tất cả 4 byte.

+0

hoặc thậm chí tất cả 8 byte, trong một thời gian dài :) – JeeBee

+1

nếu bạn thực sự đang xem xét loại quản lý bộ nhớ này, tôi nghĩ bạn nên sử dụng C++ hoặc một số ngôn ngữ khác cho phép bạn tự quản lý bộ nhớ. Bạn sẽ mất nhiều hơn trong chi phí của JVM hơn là bạn sẽ tiết kiệm được thông qua các thủ thuật như thế này trong Java. – rmeador

+0

Ah. Trong C/C++ trên các hệ thống 32 bit int và long đều là 32 bit hoặc 4 byte; Tôi quên rằng dài thực sự là một dài trên các hệ thống khác - luôn luôn làm cho tôi cười khi họ thêm "longlong" để chỉ ra một 8byte dài ... ah tốt. –

5

Bài tập tiết lộ là chạy javap trên một số mã thực hiện những việc đơn giản với byte và int. Bạn sẽ thấy các bytecode mong đợi các tham số int hoạt động trên các byte và các bytecode được chèn vào để co-erce từ cái này sang cái khác. Lưu ý rằng các mảng byte không được lưu trữ dưới dạng mảng giá trị 4 byte, do đó mảng byte có độ dài 1024 sẽ sử dụng 1k bộ nhớ (Bỏ qua bất kỳ chi phí nào).

14

Có, biến byte thực tế là 4 byte trong bộ nhớ. Tuy nhiên điều này không đúng đối với mảng. Một mảng byte 20 byte trên thực tế chỉ có 20 byte trong bộ nhớ. Đó là bởi vì ngôn ngữ Java Bytecode chỉ biết ints và longs như kiểu số (vì vậy nó phải xử lý tất cả các số như cả hai, 4 byte hoặc 8 byte), nhưng nó biết mảng với mọi kích thước số có thể (vì vậy các mảng ngắn nằm trong thực tế hai byte cho mỗi mục nhập và mảng byte thực tế là một byte cho mỗi mục nhập).

+0

oh yeah, tôi quên rằng chi tiết không quá nhỏ! –

+1

Đừng quên rằng một mảng byte cũng có chi phí bình thường là một đối tượng và độ dài. Oh, và biến của bạn sau đó là một tham chiếu (4 hoặc 8 byte). Vì vậy, để thực sự có 20 byte có sẵn và hữu ích sẽ yêu cầu 36 byte, giả sử không có răng cưa. Tôi muốn dính vào 20 trường byte :) –

+0

@Jon @Mecki Bạn có thể cung cấp công thức chính xác hoặc ít hơn để tính toán kích thước của mảng 'int []' không? Nó sẽ là '4 [= chiều dài] + 4 [= int_size] * chiều dài (mảng) + 8_byte_align'? –

2

byte = 8bit = một byte được xác định bởi Java Spec.

số bộ nhớ mà mảng byte cần là không được xác định bởi Spec, cũng không được xác định bao nhiêu đối tượng phức tạp cần.

Đối với Sun JVM tôi ghi nhận các quy tắc: https://www.sdn.sap.com/irj/sdn/weblogs?blog=/pub/wlg/5163

0

Xem MonitoringTools của tôi tại trang web của tôi (www.csd.uoc.gr/~andreou)

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

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

(Nó có thể được sử dụng để biết thêm đối tượng phức tạp/đồ thị đối tượng quá)

Trong 1.6 JDK của Sun, có vẻ như một byte thực sự mất một byte (trong phiên bản cũ hơn, int ~ byte về bộ nhớ). Nhưng lưu ý rằng ngay cả trong các phiên bản cũ hơn, byte [] cũng được đóng gói thành một byte cho mỗi mục nhập.

Dù sao thì, vấn đề là không cần thiết phải thử nghiệm phức tạp như Jon Skeet ở trên, chỉ đưa ra các ước tính. Chúng ta có thể trực tiếp đo kích thước của một vật thể!

+0

Liên kết của bạn bị hỏng – Pacerier

+0

Tôi đoán nó hiện đang hoạt động [ở đây] (http://code.google.com/p/memory-measurer/) – magiconair

0

Đọc qua các ý kiến ​​trên, có vẻ như kết luận của tôi sẽ đến như là một bất ngờ đối với nhiều (nó cũng là một bất ngờ đối với tôi), vì vậy nó worths lặp đi lặp lại:

  • Kích thước cũ (int) == kích thước (byte) cho các biến giữ không, ít nhất là trong Java của Sun 6.

Thay vào đó, kích thước (byte) == 1 byte (!!)

-3

dường như câu trả lời là có khả năng phụ thuộc vào phiên bản JVM và prob của bạn ably cũng là kiến ​​trúc CPU mà bạn đang chạy. Dòng CPU của Intel thực hiện thao tác byte hiệu quả (do lịch sử CPU 8 bit của nó). Một số chip RISC yêu cầu liên kết từ (4 byte) đối với nhiều thao tác. Và phân bổ bộ nhớ có thể khác nhau đối với các biến trên ngăn xếp, các trường trong một lớp và trong một mảng.

0

Chỉ muốn chỉ ra rằng tuyên bố

bạn có thể lưu trữ không nhiều hơn 127 trong một byte java

là không thực sự chính xác.

Bạn luôn có thể lưu trữ 256 giá trị khác nhau trong một byte, do đó bạn có thể dễ dàng có phạm vi 0,2255 của mình như thể đó là một byte "chưa ký".

Tất cả phụ thuộc vào cách bạn xử lý 8 bit đó.

Ví dụ:

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

Tôi đã làm một thử nghiệm sử dụng http://code.google.com/p/memory-measurer/ Lưu ý rằng tôi đang sử dụng 64-bit Oracle/Sun Java 6, mà không cần bất kỳ nén của tài liệu tham khảo, vv

Mỗi đối tượng chiếm một số không gian , cộng với JVM cần phải biết địa chỉ của đối tượng đó, và "địa chỉ" chính nó là 8 byte.

Với nguyên thủy, trông giống như nguyên thủy được đúc đến 64-bit cho hiệu suất tốt hơn (tất nhiên!):

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

Với Mảng:

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 

Và bây giờ đoán những gì ...

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

PS Oracle Java 6, mới nhất và lớn nhất, 64-bit, 1.6.0_37, MacOS X

Các vấn đề liên quan