Java对象的内存结构

By prince No comments

对象保存在内存中时,由以下三部分组成:

1,对象头
2,实例数据
3,对齐填充字节

一,对象头

对象头的大小一般和系统的位数有关,也和启动参数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的倍数,没有特别的功能。

发表评论

 

此站点使用Akismet来减少垃圾评论。了解我们如何处理您的评论数据