概述
在實際應用中,尤其是在進行JVM調優時,理解并正確估計對象大小是非常重要的,因為這直接影響到內存分配、垃圾回收效率以及應用程序的整體性能。
對象的組成
在Java中,計算一個對象的大小是為了了解它在內存中占用的確切空間。Java對象在Java虛擬機(JVM)中的內存布局主要包括三個部分:
- 對象頭(Header):
對象頭通常包含用于存儲對象自身的運行時元數據,如哈希碼、鎖狀態標志、線程持有的鎖指針等信息,這部分稱為mark word。
另外,對象頭還可能包含指向類元數據的指針,用于方法調度和類型信息識別。 - 實例數據(Instance Data):
這是對象的實際有效載荷,包括所有字段變量(成員變量)。每個字段根據其類型有不同的大小,基本類型的大小固定,而引用類型的大小則依賴于JVM實現,通常是固定的引用寬度(如32位或64位環境下的4字節或8字節)。 - 對齊填充(Padding):
為了滿足特定JVM或操作系統對于內存分配的要求(比如某些硬件平臺要求內存地址必須是某個字節數的倍數),JVM會在對象實例數據末尾填充額外的字節,確保整個對象占用的空間是對齊的。
Java中的基本數據類型及其大小
- 整數類型:
byte: 8位(1字節),有符號整數,范圍從-128到127。
short: 16位(2字節),有符號整數,范圍從-32,768到32,767。
int: 32位(4字節),有符號整數,范圍從-231到231-1(即-2,147,483,648到2,147,483,647)。
long: 64位(8字節),有符號整數,范圍從-263到263-1。 - 浮點類型:
float: 32位(4字節),單精度浮點數。
double: 64位(8字節),雙精度浮點數。 - 字符類型:
char: 16位(2字節),無符號Unicode字符,范圍從\u0000到\uffff。 - 布爾類型:
boolean: 在Java虛擬機中沒有明確規定其確切的大小,但通常被視為占據至少一個比特位。然而,在內存分配時,它往往會被編譯器優化成字節存儲。
需要注意的是,盡管硬件架構可能不同,但在Java虛擬機中,上述基本數據類型的大小是固定的,并不依賴于運行Java程序的具體平臺。
計算大小方式
要精確計算Java對象的大小,可以使用以下幾種方法:
- Instrumentation API: Java的java.lang.instrument.Instrumentation 接口提供了諸如getObjectSize()這樣的方法,允許在運行時獲取對象的精確大小。
- JDK1.8有一個類
jdk.nashorn.internal.ir.debug.ObjectSizeCalculator
可以評估出對象的大小,直接調用靜態方法ObjectSizeCalculator.getObjectSize - 第三方工具: 使用諸如JOL (Java Object Layout) 或 VisualVM 等工具分析堆內存,它們能夠展示出對象的具體內存布局以及占用大小。
- 手動估算: 根據上述組成原理,可以逐個累加各字段的大小來估算對象頭加上實例數據的大小,但這種方法很難考慮到具體的JVM實現細節以及對齊填充的影響。
計算大小實戰
這里使用第三方工具JOL
import jdk.nashorn.internal.ir.debug.ObjectSizeCalculator;
import org.openjdk.jol.info.ClassLayout;public class ObjectSize {public static void main(String[] args) {System.out.println("只打印大小");System.out.println(ObjectSizeCalculator.getObjectSize(new Object()));System.out.println(ObjectSizeCalculator.getObjectSize(new String()));System.out.println(ObjectSizeCalculator.getObjectSize(new int[]{}));System.out.println(ObjectSizeCalculator.getObjectSize(new Integer(1)));System.out.println("打印對象的內存大小占用情況,詳細版本");ClassLayout layout = ClassLayout.parseInstance(new Object());System.out.println(layout.toPrintable());System.out.println();ClassLayout layout2 = ClassLayout.parseInstance(new A());System.out.println(layout2.toPrintable());System.out.println();ClassLayout layout1 = ClassLayout.parseInstance(new int[]{});System.out.println(layout1.toPrintable());}public static class A {private Integer age;private String name;private Double balance;private Boolean sex;}
}
說明
以下對A對象的對象大小進行說明
總結
理解Java對象大小的重要性、組成以及計算方法,以便更好地進行內存優化和性能調優,歡迎關注:魯班曰
參考文獻
java對象在內存的大小