Câu trả lời:
Mindprod chỉ ra rằng đây không phải là một câu hỏi đơn giản để trả lời:
Một JVM có thể tự do lưu trữ dữ liệu theo bất kỳ cách nào nó làm hài lòng bên trong, endian lớn hay nhỏ, với bất kỳ số lượng đệm hoặc chi phí nào, mặc dù các nguyên thủy phải hành xử như thể chúng có kích thước chính thức.
Ví dụ, JVM hoặc trình biên dịch gốc có thể quyết định lưu trữ mộtboolean[]
đoạn dài 64 bit như aBitSet
. Nó không phải nói với bạn, miễn là chương trình đưa ra câu trả lời giống nhau.
- Nó có thể phân bổ một số Đối tượng tạm thời trên ngăn xếp.
- Nó có thể tối ưu hóa một số biến hoặc các cuộc gọi phương thức hoàn toàn không tồn tại thay thế chúng bằng hằng số.
- Nó có thể phiên bản phương thức hoặc vòng lặp, tức là biên dịch hai phiên bản của một phương thức, mỗi phiên bản được tối ưu hóa cho một tình huống nhất định, sau đó quyết định trước cái nào sẽ gọi.
Sau đó, tất nhiên phần cứng và HĐH có bộ đệm đa lớp, trên bộ đệm chip, bộ đệm SRAM, bộ đệm DRAM, bộ làm việc RAM thông thường và lưu trữ sao lưu trên đĩa. Dữ liệu của bạn có thể được nhân đôi ở mọi cấp độ bộ đệm. Tất cả sự phức tạp này có nghĩa là bạn chỉ có thể dự đoán rất rõ mức tiêu thụ RAM.
Bạn có thể dùng Instrumentation.getObjectSize()
để có được ước tính dung lượng lưu trữ được sử dụng bởi một đối tượng.
Để trực quan hóa bố cục đối tượng, dấu chân và tham chiếu thực tế , bạn có thể sử dụng công cụ JOL (Bố cục đối tượng Java) .
Trong JDK 64 bit hiện đại, một đối tượng có tiêu đề 12 byte, được đệm thành bội số của 8 byte, vì vậy kích thước đối tượng tối thiểu là 16 byte. Đối với các JVM 32 bit, tổng phí là 8 byte, được đệm thành bội số của 4 byte. (Từ câu trả lời Dmitry Spikhalskiy của , câu trả lời Jayen của , và JavaWorld .)
Thông thường, các tham chiếu là 4 byte trên nền tảng 32 bit hoặc trên nền tảng 64 bit trở lên -Xmx32G
; và 8 byte trên 32Gb ( -Xmx32G
). (Xem tài liệu tham khảo đối tượng nén .)
Kết quả là, một JVM 64 bit thường sẽ cần thêm không gian heap 30-50%. ( Tôi nên sử dụng JVM 32 hoặc 64 bit?, 2012, JDK 1.7)
Các hàm bao đóng hộp có chi phí hoạt động so với các kiểu nguyên thủy (từ JavaWorld ):
Integer
: Kết quả 16 byte kém hơn một chút so với tôi dự đoán vì mộtint
giá trị có thể vừa với 4 byte bổ sung. Việc sử dụngInteger
chi phí cho tôi là 300% bộ nhớ so với khi tôi có thể lưu trữ giá trị dưới dạng nguyên thủy
Long
: 16 byte cũng: Rõ ràng, kích thước đối tượng thực tế trên heap phải tuân theo căn chỉnh bộ nhớ mức thấp được thực hiện bởi một triển khai JVM cụ thể cho một loại CPU cụ thể. Dường như aLong
là 8 byte chi phí đối tượng, cộng thêm 8 byte cho giá trị dài thực tế. Ngược lại,Integer
có một lỗ 4 byte không được sử dụng, rất có thể là do JVM I sử dụng lực lượng căn chỉnh đối tượng trên một ranh giới từ 8 byte.
Các container khác cũng rất tốn kém:
Mảng nhiều chiều : nó cung cấp một bất ngờ khác.
Các nhà phát triển thường sử dụng các cấu trúc nhưint[dim1][dim2]
trong máy tính số và khoa học.Trong một
int[dim1][dim2]
trường hợp mảng, mỗiint[dim2]
mảng lồng nhau là mộtObject
quyền riêng của nó. Mỗi bổ sung thêm mảng 16 byte thông thường. Khi tôi không cần một mảng hình tam giác hoặc rách rưới, nó đại diện cho chi phí thuần túy. Tác động tăng lên khi kích thước mảng khác nhau rất nhiều.Ví dụ, một
int[128][2]
cá thể mất 3.600 byte. So với 1.040 byte mà mộtint[256]
cá thể sử dụng (có cùng dung lượng), 3.600 byte đại diện cho chi phí hoạt động là 246 phần trăm. Trong trường hợp cực đoanbyte[256][1]
, hệ số trên không là 19! So sánh điều đó với tình huống C / C ++ trong đó cùng một cú pháp không thêm bất kỳ chi phí lưu trữ nào.
String
:String
sự tăng trưởng bộ nhớ của a theo dõi sự tăng trưởng của mảng char bên trong của nó. Tuy nhiên,String
lớp thêm 24 byte chi phí khác.Đối với số không
String
có kích thước từ 10 ký tự trở xuống, chi phí phải trả thêm vào tương ứng với tải trọng hữu ích (2 byte cho mỗi char cộng với 4 byte cho chiều dài), dao động từ 100 đến 400 phần trăm.
Xem xét đối tượng ví dụ này :
class X { // 8 bytes for reference to the class definition
int a; // 4 bytes
byte b; // 1 byte
Integer c = new Integer(); // 4 bytes for a reference
}
Một khoản tiền ngây thơ sẽ gợi ý rằng một thể hiện X
sẽ sử dụng 17 byte. Tuy nhiên, do căn chỉnh (còn được gọi là phần đệm), JVM phân bổ bộ nhớ theo bội số 8 byte, vì vậy thay vì 17 byte, nó sẽ phân bổ 24 byte.
Nó phụ thuộc vào kiến trúc / jdk. Đối với kiến trúc JDK và 64 bit hiện đại, một đối tượng có tiêu đề 12 byte và đệm thêm 8 byte - vì vậy kích thước đối tượng tối thiểu là 16 byte. Bạn có thể sử dụng một công cụ gọi là Java Object Layout để xác định kích thước và nhận chi tiết về bố cục đối tượng và cấu trúc bên trong của bất kỳ thực thể nào hoặc đoán thông tin này bằng cách tham chiếu lớp. Ví dụ về đầu ra cho Integer trên môi trường của tôi:
Running 64-bit HotSpot VM.
Using compressed oop with 3-bit shift.
Using compressed klass with 3-bit shift.
Objects are 8 bytes aligned.
Field sizes by type: 4, 1, 1, 2, 2, 4, 4, 8, 8 [bytes]
Array element sizes: 4, 1, 1, 2, 2, 4, 4, 8, 8 [bytes]
java.lang.Integer object internals:
OFFSET SIZE TYPE DESCRIPTION VALUE
0 12 (object header) N/A
12 4 int Integer.value N/A
Instance size: 16 bytes (estimated, the sample instance is not available)
Space losses: 0 bytes internal + 0 bytes external = 0 bytes total
Vì vậy, đối với Integer, kích thước cá thể là 16 byte, vì int 4 byte được nén tại chỗ ngay sau tiêu đề và trước ranh giới đệm.
Mẫu mã:
import org.openjdk.jol.info.ClassLayout;
import org.openjdk.jol.util.VMSupport;
public static void main(String[] args) {
System.out.println(VMSupport.vmDetails());
System.out.println(ClassLayout.parseClass(Integer.class).toPrintable());
}
Nếu bạn sử dụng maven, để lấy JOL:
<dependency>
<groupId>org.openjdk.jol</groupId>
<artifactId>jol-core</artifactId>
<version>0.3.2</version>
</dependency>
Mỗi đối tượng có một chi phí nhất định cho màn hình liên quan và thông tin loại, cũng như chính các trường. Ngoài ra, các trường có thể được trình bày khá nhiều tuy nhiên JVM thấy phù hợp (tôi tin) - nhưng như trong câu trả lời khác , ít nhất một số JVM sẽ đóng gói khá chặt chẽ. Hãy xem xét một lớp học như thế này:
public class SingleByte
{
private byte b;
}
đấu với
public class OneHundredBytes
{
private byte b00, b01, ..., b99;
}
Trên JVM 32 bit, tôi mong đợi 100 trường hợp SingleByte
sẽ nhận 1200 byte (8 byte trên cao + 4 byte cho trường do đệm / căn chỉnh). Tôi mong đợi một phiên bản OneHundredBytes
lấy 108 byte - chi phí chung, và sau đó 100 byte, được đóng gói. Nó chắc chắn có thể thay đổi theo JVM - một triển khai có thể quyết định không đóng gói các trường trongOneHundredBytes
, dẫn đến nó mất 408 byte (= 8 byte trên đầu + 4 * 100 byte được liên kết / đệm). Trên JVM 64 bit, chi phí cũng có thể lớn hơn (không chắc chắn).
EDIT: Xem bình luận dưới đây; rõ ràng các miếng đệm HotSpot đến ranh giới 8 byte thay vì 32, vì vậy mỗi phiên bản SingleByte
sẽ lấy 16 byte.
Dù bằng cách nào, "đối tượng lớn duy nhất" sẽ ít nhất có hiệu quả như nhiều đối tượng nhỏ - đối với các trường hợp đơn giản như thế này.
Tổng bộ nhớ đã sử dụng / miễn phí của một chương trình có thể được lấy trong chương trình thông qua
java.lang.Runtime.getRuntime();
Thời gian chạy có một số phương thức liên quan đến bộ nhớ. Ví dụ mã hóa sau đây chứng minh việc sử dụng nó.
package test;
import java.util.ArrayList;
import java.util.List;
public class PerformanceTest {
private static final long MEGABYTE = 1024L * 1024L;
public static long bytesToMegabytes(long bytes) {
return bytes / MEGABYTE;
}
public static void main(String[] args) {
// I assume you will know how to create a object Person yourself...
List < Person > list = new ArrayList < Person > ();
for (int i = 0; i <= 100000; i++) {
list.add(new Person("Jim", "Knopf"));
}
// Get the Java runtime
Runtime runtime = Runtime.getRuntime();
// Run the garbage collector
runtime.gc();
// Calculate the used memory
long memory = runtime.totalMemory() - runtime.freeMemory();
System.out.println("Used memory is bytes: " + memory);
System.out.println("Used memory is megabytes: " + bytesToMegabytes(memory));
}
}
Dường như mọi đối tượng đều có tổng phí 16 byte trên các hệ thống 32 bit (và 24 byte trên hệ thống 64 bit).
http://algs4.cs.princeton.edu/14analysis/ là một nguồn thông tin tốt. Một ví dụ trong số nhiều người tốt là sau đây.
http://www.cs.virginia.edu/kim/publicity/pldi09tutorials/memory-ffic-java-tutorial.pdf cũng rất nhiều thông tin, ví dụ:
Có phải không gian bộ nhớ được tiêu thụ bởi một đối tượng với 100 thuộc tính giống với 100 đối tượng, mỗi thuộc tính không?
Không.
Bao nhiêu bộ nhớ được phân bổ cho một đối tượng?
Bao nhiêu không gian bổ sung được sử dụng khi thêm một thuộc tính?
Câu hỏi sẽ là một câu hỏi rất rộng.
Nó phụ thuộc vào biến lớp hoặc bạn có thể gọi là trạng thái sử dụng bộ nhớ trong java.
Nó cũng có một số yêu cầu bộ nhớ bổ sung cho các tiêu đề và tham chiếu.
Bộ nhớ heap được sử dụng bởi một đối tượng Java bao gồm
bộ nhớ cho các trường nguyên thủy, theo kích thước của chúng (xem bên dưới để biết Kích cỡ của các kiểu nguyên thủy);
bộ nhớ cho các trường tham chiếu (mỗi 4 byte);
một tiêu đề đối tượng, bao gồm một vài byte thông tin "dọn phòng";
Các đối tượng trong java cũng yêu cầu một số thông tin "dọn phòng", chẳng hạn như ghi lại lớp, ID và các cờ trạng thái của đối tượng, chẳng hạn như liệu đối tượng có thể truy cập được không, hiện đang bị khóa đồng bộ hóa, v.v.
Kích thước tiêu đề đối tượng Java thay đổi trên jvm 32 và 64 bit.
Mặc dù đây là những bộ nhớ chính mà người tiêu dùng jvm cũng yêu cầu các trường bổ sung đôi khi như để căn chỉnh mã, v.v.
Kích cỡ của các loại nguyên thủy
boolean & byte - 1
char & ngắn - 2
int & float - 4
dài & đôi - 8
Tôi đã nhận được kết quả rất tốt từ cách tiếp cận java.lang.instrument.Instrumentation được đề cập trong một câu trả lời khác. Để biết các ví dụ hay về việc sử dụng nó, hãy xem mục, Bộ đếm bộ nhớ thiết bị từ Bản tin của Chuyên gia Java và thư viện java.sizeOf trên SourceForge.
Trong trường hợp nó hữu ích với bất kỳ ai, bạn có thể tải xuống từ trang web của tôi một tác nhân Java nhỏ để truy vấn việc sử dụng bộ nhớ của một đối tượng . Nó cũng sẽ cho phép bạn truy vấn sử dụng bộ nhớ "sâu".
(String, Integer)
Guava Cache sử dụng, mỗi phần tử. Cảm ơn!
Các quy tắc về lượng bộ nhớ được tiêu thụ phụ thuộc vào việc triển khai JVM và kiến trúc CPU (ví dụ 32 bit so với 64 bit).
Để biết các quy tắc chi tiết cho SUN JVM, hãy kiểm tra blog cũ của tôi
Trân trọng, Markus