前言
普通對象的內存布局:
- 1)Mark Word, 8個字節(jié)
- 2)Class Pointer,如果是 32G 內存以下的,默認開啟對象指針壓縮,4 個字節(jié)
- 3)數據區(qū)
- 4)Padding(內存對齊),按照 8 的倍數對齊
數組對象的內存布局:
- 1)Mark Word, 8個字節(jié)
- 2)Class Pointer,如果是 32G 內存以下的,默認開啟對象指針壓縮,4個字節(jié)
- 3)數組長度,4 個字節(jié)
- 4)數據區(qū)
- 5)Padding(內存對齊),按照 8 的倍數對齊

對象的內存布局
原生類型內存占用如下圖所示:

原生類型占用內存
RamUsageEstimator(計算 Java 對象內存占用)
簡介
RamUsageEstimator 是根據 Java 對象在堆內存中的存儲格式,通過計算 Java 對象頭、實例數據、引用等的大小,相加而得,如果有引用,還能遞歸計算引用對象的大小。
缺點:這種方式計算所得的對象頭大小是基于 JVM 聲明規(guī)范的,并不是通過運行時內存地址計算而得,存在與實際大小不符的這種可能性。
依賴
<!-- 計算 Java 對象內存占用工具 -->
<dependency>
<groupId>org.apache.lucene</groupId>
<artifactId>lucene-core</artifactId>
<version>4.0.0</version>
</dependency>
常用方法 API
//計算指定對象及其引用樹上的所有對象的綜合大小,單位字節(jié)
long RamUsageEstimator.sizeOf(Object obj)
//計算指定對象本身在堆空間的大小,單位字節(jié)
long RamUsageEstimator.shallowSizeOf(Object obj)
//計算指定對象及其引用樹上的所有對象的綜合大小,返回可讀的結果,如:2KB
String RamUsageEstimator.humanSizeOf(Object obj)
演示代碼
sizeOf() 方法演示:
import lombok.extern.slf4j.Slf4j;
import org.apache.lucene.util.RamUsageEstimator;
@Slf4j
public class RamUsageEstimatorDemo {
public static void main(String[] args) {
// 12(Header) + 0(Instance Data) + 4(Padding) = 16 bytes
log.info("sizeOf(new Object()) = {} bytes", RamUsageEstimator.sizeOf(new Object()));
// 12(Header) + 1(Instance Data) + 3(Padding) = 16 bytes
log.info("sizeOf(boolean) = {} bytes", RamUsageEstimator.sizeOf(true));
log.info("sizeOf(byte) = {} bytes", RamUsageEstimator.sizeOf((byte)2));
// 12(Header) + 2(Instance Data) + 2(Padding) = 16 bytes
log.info("sizeOf(char) = {} bytes", RamUsageEstimator.sizeOf('c'));
log.info("sizeOf(short) = {} bytes", RamUsageEstimator.sizeOf((short)2));
// 12(Header) + 4(Instance Data) + 0(Padding) = 16 bytes
log.info("sizeOf(int) = {} bytes", RamUsageEstimator.sizeOf(2));
log.info("sizeOf(float) = {} bytes", RamUsageEstimator.sizeOf((float)2.0));
// 12(Header) + 8(Instance Data) + 4(Padding) = 24 bytes
log.info("sizeOf(long) = {} bytes", RamUsageEstimator.sizeOf((long)2));
log.info("sizeOf(double) = {} bytes", RamUsageEstimator.sizeOf(2.0));
// 16(Header) + 0(Instance Data) + 0(Padding) = 16 bytes
log.info("sizeOf(new int[]) = {} bytes", RamUsageEstimator.sizeOf(new int[]{}));
// 16(Header) + 4(Instance Data) + 4(Padding) = 24 bytes
log.info("sizeOf(new int[]) = {} bytes", RamUsageEstimator.sizeOf(new int[]{2}));
// 16(Header) + 8(Instance Data) + 0(Padding) = 24 bytes
log.info("sizeOf(new int[]) = {} bytes", RamUsageEstimator.sizeOf(new int[]{2, 2}));
}
}
shallowSizeOf() 方法演示:
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.lucene.util.RamUsageEstimator;
@Slf4j
public class RamUsageEstimatorReferenceDemo {
public static void main(String[] args) {
ReferenceData empty = new ReferenceData();
ReferenceData full = ReferenceData.full();
// 12(Header) + 8(Instance Data) + 4(Padding) = 24 bytes
// 一個壓縮后的對象指針占用 4 bytes,兩個就是 8 bytes
log.info("sizeOf(empty ReferenceData) = {} bytes", RamUsageEstimator.sizeOf(empty));
log.info("humanSizeOf(empty ReferenceData) = {} bytes", RamUsageEstimator.humanSizeOf(empty));
log.info("shallowSizeOf(empty ReferenceData) = {} bytes", RamUsageEstimator.shallowSizeOf(empty));
System.out.println();
// ReferenceData: 12(Header) + 8(Instance Data) + 4(Padding) = 24 bytes
// Integer: 12(Header) + 4(Instance Data) = 16 bytes
// Long: 12(Header) + 8(Instance Data) + 4(Padding) = 24 bytes
// total size = 24(ReferenceData) + 16(Integer) + 24(Long) + = 64 bytes
log.info("sizeOf(full ReferenceData) = {} bytes", RamUsageEstimator.sizeOf(full));
log.info("humanSizeOf(full ReferenceData) = {} bytes", RamUsageEstimator.humanSizeOf(full));
// shallowSizeOf() 方法不會考慮字段引用對象占用的內存
// 一個壓縮后的對象指針占用 4 bytes,兩個就是 8 bytes
// 12(Header) + 8(Instance Data) + 4(Padding) = 24 bytes
log.info("shallowSizeOf(full ReferenceData) = {} bytes", RamUsageEstimator.shallowSizeOf(full));
}
@Data
@AllArgsConstructor
@NoArgsConstructor
public static class ReferenceData {
private Integer intVal;
private Long longVal;
public static ReferenceData full() {
return new ReferenceData(2, 2L);
}
}
}
jol(查看對象頭的神器)
簡介
jol 為 java object layout 的縮寫,即 Java 對象布局。
是一個可以在代碼中計算 Java 對象的大小以及查看 Java 對象內存布局的工具包。
依賴
<!-- 查看對象頭的神器 -->
<dependency>
<groupId>org.openjdk.jol</groupId>
<artifactId>jol-core</artifactId>
<version>0.14</version>
</dependency>
使用 jol 計算對象的大小
語法:
// 使用 jol 計算對象的大?。▎挝粸樽止?jié))
ClassLayout.parseInstance(obj).instanceSize()
使用 Demo:
import lombok.extern.slf4j.Slf4j;
import org.openjdk.jol.info.ClassLayout;
@Slf4j
public class ClassLayoutDemo {
public static void main(String[] args) {
// 12(Header) + 0(Instance Data) + 4(Padding) = 16 bytes
log.info("sizeOf(new Object()) = {} bytes", ClassLayout.parseInstance(new Object()).instanceSize());
// 12(Header) + 1(Instance Data) + 3(Padding) = 16 bytes
log.info("sizeOf(boolean) = {} bytes", ClassLayout.parseInstance(true).instanceSize());
log.info("sizeOf(byte) = {} bytes", ClassLayout.parseInstance((byte) 2).instanceSize());
// 12(Header) + 2(Instance Data) + 2(Padding) = 16 bytes
log.info("sizeOf(char) = {} bytes", ClassLayout.parseInstance('c').instanceSize());
log.info("sizeOf(short) = {} bytes", ClassLayout.parseInstance((short) 2).instanceSize());
// 12(Header) + 4(Instance Data) + 0(Padding) = 16 bytes
log.info("sizeOf(int) = {} bytes", ClassLayout.parseInstance(2).instanceSize());
log.info("sizeOf(float) = {} bytes", ClassLayout.parseInstance((float) 2.0).instanceSize());
// 12(Header) + 8(Instance Data) + 4(Padding) = 24 bytes
log.info("sizeOf(long) = {} bytes", ClassLayout.parseInstance((long) 2).instanceSize());
log.info("sizeOf(double) = {} bytes", ClassLayout.parseInstance(2.0).instanceSize());
// 16(Header) + 0(Instance Data) + 0(Padding) = 16 bytes
log.info("sizeOf(new int[]) = {} bytes", ClassLayout.parseInstance(new int[]{}).instanceSize());
// 16(Header) + 4(Instance Data) + 4(Padding) = 24 bytes
log.info("sizeOf(new int[]) = {} bytes", ClassLayout.parseInstance(new int[]{2}).instanceSize());
// 16(Header) + 8(Instance Data) + 0(Padding) = 24 bytes
log.info("sizeOf(new int[]) = {} bytes", ClassLayout.parseInstance(new int[]{2, 2}).instanceSize());
}
}
和 RamUsageEstimator.sizeOf() 方法的結果一致。
使用 jol 查看對象的內存布局
語法:
// 使用 jol 查看對象的內存布局
ClassLayout.parseInstance(obj).toPrintable()
使用 Demo:
import org.openjdk.jol.info.ClassLayout;
public class ClassLayoutDemo {
public static void main(String[] args) {
// 12(Header) + 0(Instance Data) + 4(Padding) = 16 bytes
System.out.println(ClassLayout.parseInstance(new Object()).toPrintable());
// 12(Header) + 1(Instance Data) + 3(Padding) = 16 bytes
System.out.println(ClassLayout.parseInstance(true).toPrintable());
System.out.println(ClassLayout.parseInstance((byte) 2).toPrintable());
// 12(Header) + 2(Instance Data) + 2(Padding) = 16 bytes
System.out.println(ClassLayout.parseInstance('c').toPrintable());
System.out.println(ClassLayout.parseInstance((short) 2).toPrintable());
// 12(Header) + 4(Instance Data) + 0(Padding) = 16 bytes
System.out.println(ClassLayout.parseInstance(2).toPrintable());
System.out.println(ClassLayout.parseInstance((float) 2.0).toPrintable());
// 12(Header) + 8(Instance Data) + 4(Padding) = 24 bytes
System.out.println(ClassLayout.parseInstance((long) 2).toPrintable());
System.out.println(ClassLayout.parseInstance(2.0).toPrintable());
// 16(Header) + 0(Instance Data) + 0(Padding) = 16 bytes
System.out.println(ClassLayout.parseInstance(new int[]{}).toPrintable());
// 16(Header) + 4(Instance Data) + 4(Padding) = 24 bytes
System.out.println(ClassLayout.parseInstance(new int[]{2}).toPrintable());
// 16(Header) + 8(Instance Data) + 0(Padding) = 24 bytes
System.out.println(ClassLayout.parseInstance(new int[]{2, 2}).toPrintable());
}
}
打印結果:
java.lang.Object object internals:
OFFSET SIZE TYPE DESCRIPTION VALUE
0 4 (object header) 01 00 00 00 (00000001 00000000 00000000 00000000) (1)
4 4 (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0)
8 4 (object header) e5 01 00 f8 (11100101 00000001 00000000 11111000) (-134217243)
12 4 (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total
java.lang.Boolean object internals:
OFFSET SIZE TYPE DESCRIPTION VALUE
0 4 (object header) 01 00 00 00 (00000001 00000000 00000000 00000000) (1)
4 4 (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0)
8 4 (object header) dc 20 00 f8 (11011100 00100000 00000000 11111000) (-134209316)
12 1 boolean Boolean.value true
13 3 (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 3 bytes external = 3 bytes total
...
[I object internals:
OFFSET SIZE TYPE DESCRIPTION VALUE
0 4 (object header) 01 00 00 00 (00000001 00000000 00000000 00000000) (1)
4 4 (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0)
8 4 (object header) 6d 01 00 f8 (01101101 00000001 00000000 11111000) (-134217363)
12 4 (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0)
16 0 int [I.<elements> N/A
Instance size: 16 bytes
Space losses: 0 bytes internal + 0 bytes external = 0 bytes total