Java对象的内存结构
对象保存在内存中时,由以下三部分组成:
一,对象头
对象头的大小一般和系统的位数有关,也和启动参数UseCompressedOops有关:
32位系统,占用 8 字节
64位系统,开启UseCompressedOops时,占用 12 字节,否则是16字节
java的对象头由以下三部分组成:
1,Mark Word
2,指向类的指针
3,数组长度(只有数组对象才有)
1,Mark Word
Mark Word记录了对象和锁有关的信息,当这个对象被synchronized关键字当成同步锁时,围绕这个锁的一系列操作都和Mark Word有关。
Mark Word在32位JVM中的长度是4个字节,在64位JVM中长度是8个字节。
Mark Word在不同的锁状态下存储的内容不同,在32位JVM中是这么存的:
在HotSpot的源码中我们可以找到关于对象头对象的定义,会一一印证上图的描述。对应与markOop.hpp类。
enum { age_bits = 4, lock_bits = 2, biased_lock_bits = 1, max_hash_bits = BitsPerWord -age_bits-lock_bits- biased_lock_bits, hash_bits = max_hash_bits > 31 ? 31 : max_hash_bits, cms_bits = LP64_ONLY(1) NOT_LP64(0), epoch_bits = 2 };
从上面的枚举定义中可以看出,对象头中主要包含了GC分代年龄、锁状态标记、哈希码、epoch等信息。
从上图中可以看出,对象的状态一共有五种,分别是无锁态、轻量级锁、重量级锁、GC标记和偏向锁。在32位的虚拟机中有两个Bits是用来存储锁的标记为的,但是我们都知道,两个bits最多只能表示四种状态:00、01、10、11,那么第五种状态如何表示呢 ,就要额外依赖1Bit的空间,使用0和1来区分。
在32位的HotSpot虚拟机 中对象未被锁定的状态下,Mark Word的32个Bits空间中的25Bits用于存储对象哈希码(HashCode),4Bits用于存储对象分代年龄,2Bits用于存储锁标志位,1Bit固定为0,表示非偏向锁。
markOop.hpp类中有关于对象状态的定义:
enum { locked_value = 0, unlocked_value = 1, monitor_value = 2, marked_value = 3, biased_lock_pattern = 5 };
简单翻译一下:
locked_value(00) = 0
unlocked_value(01) = 1
monitor_value(10) = 2
marked_value(11) = 3
biased_lock_pattern(101) = 5
2,指向类的指针
该指针在32位JVM中的长度是4个字节,在64位JVM中长度是8个字节。
Java对象的类数据保存在方法区。
3,数组长度
只有数组对象保存了这部分数据。
该数据在32位和64位JVM中长度都是4字节。
二,实例数据
对象的实例数据就是在java代码中能看到的属性和他们的值。
原生类型的内存占用情况如下:
boolean 1
byte 1
short 2
char 2
int 4
float 4
long 8
double 8
引用类型的内存占用和系统位数以及启动参数UseCompressedOops有关
32位系统占4字节
64位系统,开启UseCompressedOops时,占用4字节,否则是8字节
三,对齐填充字节
在Hotspot中,为了更加容易的管理内存,一般会使用8字节进行对齐
意思是java的对象占的内存大小应该是8的倍数,所以后面有几个字节用于把对象的大小补齐至8的倍数,没有特别的功能。