JVM运行时数据区(Runtime Data Area)分为JVM栈(Java栈)、堆区(Heap)、方法区、本地方法栈和程序计数器,而Java中的对象和数组(数组可以理解为特殊的对象)是存放在堆区的,这篇文章详细解析对象以及数组在堆区中所占空间大小。

对象在堆区中所占空间大小

结构示意图

先来看一下对象在堆区存储的结构示意图

java想存储一个数组到数据库字段_java

对象头(Object Head)

标记字和类型指针统称对象头

标记字(Mark Word)

标记字在32位机器占用4字节,在64位机器占用8字节,主要用来表示对象的线程锁状态,还可以用来配合GC、存放该对象的 HashCode。

类型指针(Klass Pointer)

类型指针即当前对象所属类型的指针,封装了语言级别的类信息,例如类名、其修饰符、超类信息等。

加载类的时候类的加载器会将.class字节码文件加载到内存中,即创建一个该类的Class实例,用来表示这个类的元数据,包括常量池、字段、方法等,存放在方法区。

而类型指针(Klass Pointer)就是指向方法区中的Class实例。

类型指针所占的空间大小跟JVM的一个参数“类型指针压缩”(UseCompressedClassPointers)有关,

类型指针压缩参数在开启时类型指针是占4个字节,关闭时类型指针是占8个字节。请注意,该参数是默认开启的,也就是说类型指针默认被压缩为4个字节。(在64位机器上)

在32位机器上,类型指针占用一个字,即4个字节。

实例数据(Instance Data) / 对象体(Object Body)

该部分存储本对象的实例数据(即未被static修饰的变量),实例数据又分为基本数据类型和引用类型。

而静态变量(即被static修饰的变量)不存储在实例对象中,存放在方法区中的Class实例的尾部,被该类的所有对象所共享。

基本数据类型

基本数据类型及所占字节数:

boolean:1字节

char:2字节

byte:1字节

short:2字节

int:4字节

long:8字节

float:4字节

double:8字节

引用数据类型

HotSpot JVM 使用称为普通对象指针 (OOPS, Ordinary Object Pointers) 的数据结构来表示对象指针(即引用数据类型)。它的大小与JVM的另外一个参数“普通对象指针压缩”(UseCompressedOops)有关。

在普通对象指针压缩参数开启时引用占4个字节,关闭时引用占8个字节。

对齐(Padding) / 填充物

由于JVM规定每个对象占用的总空间是8的倍数,故对象占用的总空间(对象头+对象体)不是8的倍数时,会自动补齐字节,使整个对象加上对齐所占用的空间为8的倍数。

例子

public class Test {
    int i;
    long l;
    Long L;
}

在默认开启两个压缩参数时,该类的一个对象在堆占的内存为8(标记字)+4(被压缩的类型指针)+4(int)+8(long)+4(被压缩的包装类引用Long)+4(将前面的28字节对齐,补为8的倍数32字节)

数组在堆区中所占空间大小

结构示意图

先来看一下数组在堆区存储的结构示意图 

java想存储一个数组到数据库字段_java_02

数组与对象唯一的区别是只比对象多了一个占4字节的数组长度(Length),其他均与对象相同。

例子 

默认开启两个压缩参数时,

new Double[10] = 8(Mark Word) + 4(压缩后的Klass Pointer) + 4(Length,4字节长的数组长度记录数组的长度) + 10 * 4(Instance Data,10个被压缩后的包装类引用Double,每个占4字节) + 0(长度为56是8的倍数,不用对齐) = 56 Bytes

new char[7] = 8(Mark Word) + 4(压缩后的Klass Pointer) + 4(Length,4字节长的数组长度记录数组的长度) + 7 * 2(Instance Data,7个基本数据类型char,每个占2字节) + 2(将前面的30字节补位8的倍数32字节) = 32 Bytes