前言
普通對象的內(nèi)存布局:
- 1)Mark Word, 8個字節(jié)
- 2)Class Pointer,如果是 32G 內(nèi)存以下的饮怯,默認開啟對象指針壓縮蝌衔,4 個字節(jié)
- 3)數(shù)據(jù)區(qū)
- 4)Padding(內(nèi)存對齊)榛泛,按照 8 的倍數(shù)對齊
數(shù)組對象的內(nèi)存布局:
- 1)Mark Word, 8個字節(jié)
- 2)Class Pointer噩斟,如果是 32G 內(nèi)存以下的曹锨,默認開啟對象指針壓縮,4個字節(jié)
- 3)數(shù)組長度剃允,4 個字節(jié)
- 4)數(shù)據(jù)區(qū)
- 5)Padding(內(nèi)存對齊)沛简,按照 8 的倍數(shù)對齊
原生類型內(nèi)存占用如下圖所示:
RamUsageEstimator(計算 Java 對象內(nèi)存占用)
簡介
RamUsageEstimator
是根據(jù) Java 對象在堆內(nèi)存中的存儲格式,通過計算 Java 對象頭斥废、實例數(shù)據(jù)椒楣、引用等的大小,相加而得牡肉,如果有引用捧灰,還能遞歸計算引用對象的大小。
缺點:這種方式計算所得的對象頭大小是基于 JVM 聲明規(guī)范的统锤,并不是通過運行時內(nèi)存地址計算而得毛俏,存在與實際大小不符的這種可能性炭庙。
依賴
<!-- 計算 Java 對象內(nèi)存占用工具 -->
<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() 方法不會考慮字段引用對象占用的內(nèi)存
// 一個壓縮后的對象指針占用 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 對象內(nèi)存布局的工具包迹卢。
依賴
<!-- 查看對象頭的神器 -->
<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 查看對象的內(nèi)存布局
語法:
// 使用 jol 查看對象的內(nèi)存布局
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