Java对象内存布局

java内存对齐结构体 方式 java 对象内存布局_java

位置

存储内容

所占大小

对象头

8字节的标志位,存储分代年龄,hashcode(获取才会存储)等标志

8字节的classpointer,指向类的元数据地址,方法区

如果是数组对象,4字节存储数组的大小长度,如果不是数组对象,则不包含

8字节(64位系统)

+8字节

+4字节

实例数据

存储着对象包含的所有成员变量,大小根据不同的数据类型空间不同

boolean和byte是1字节

short和char是2字节

int和float是4字节

long和double是8字节

引用类型是8字节

填充数据

将对象的所占空间填充为8字节的整数倍,以适配CPU的读取,是空间换时间的思想

目的

了解Java的对象布局,可以在相关高并发场景下,计算出会产生多大的内存,用来调整JVM大小

工具JOL Demo

maven依赖
<dependency>
  <groupId>org.openjdk.jol</groupId>
  <artifactId>jol-core</artifactId>
  <version>RELEASE</version>
</dependency>
demo代码
package com.bayitalk.clazz;
import org.openjdk.jol.info.ClassLayout;
/**
* 测试对象的布局
*/
public class TClass {
    private final static int a = 20;
    private static int b = 129;
    private int c = 12;
  	private int d = 10;
    private static String comment = "This is Test class";
    public String getComment(){
        return comment;
    }
    public static void main(String[] args) {
        System.out.println(ClassLayout.parseInstance(new TClass()).toPrintable());
    }
}
输出结果
com.bayitalk.clazz.TClass object internals:
OFF  SZ   TYPE DESCRIPTION               VALUE
  0   8        (object header: mark)     0x0000000000000001 (non-biasable; age: 0)
  8   4        (object header: class)    0xf800c105
 12   4    int TClass.c                  12
 16   4    int TClass.d                  10
 20   4        (object alignment gap)    
Instance size: 24 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total
解析
  1. 8个字节的标志位,并且是大端存储
  2. 标志位中并没有hashcode的值,是因为hashcode只有调用了hashcode相关的方法才会存储进标识位
  3. class pointer是4个字节,因为存在指针压缩,为了减少空间和减少GC的频次
  4. 实例对象只存储了成员变量
  5. 有4个字节的填充字段,用来填充成8字节的整数倍
备注

小端存储:便于数据类型转换,例如long转int可以直接舍弃

大端存储:便于符号判断,最低位是符号位

指针压缩超过32G会失效。32位的处理器,内存大概4G

ClassPointer

classpointer存储的是指向类元数据(方法区)的地址,因此Java采用的是直接指针访问对象.

句柄池访问对象

java内存对齐结构体 方式 java 对象内存布局_句柄_02

直接指针指向

java内存对齐结构体 方式 java 对象内存布局_java_03

句柄池和直接引用的区别

访问方式

定义

优劣势

句柄池访问对象

句柄池访问,会再堆中单独开辟一个空间,存储对象实例数据和类元类型数据的指针

优势:reference存储的是句柄的地址,在对象实例移动的时候,不需要维护reference,只需要修改句柄池就好。

劣势:需要额外的存储空间,增加了一次指针跳转的开销

直接指针指向

在对象的实例数据中直接存储类元类型数据的指针

优势:减少了一次指针定位的开销

劣势:在GC等移动复制对象的时候,需要维护相应的reference

指针压缩

  • 为了保证CPU缓存,存储更多的对象指针,将8字节压缩成4字节
  • 减少GC发生的频次,占更少的资源
  • 关闭指针压缩 : -XX:-UseCompressedOops
  • 64位系统,4G内存默认开启指针压缩,超过32G之后,指针压缩失效,32位系统的CPU 最大支持2^32 = 4G ,如果是64位系统,最大支持 2^64, 但是对其填充是按照8字节进行填充,指针压缩可以理解为在32位系统在64位上面使用,因为32位系统的CPU寻址空间最大支持4G,对其进行8字节填充 = 32G,这就是内存超过32G指针压缩失效的原因。

对齐填充

  • 对齐填充,是为了提高CPU的访问效率
  • 因此对对象的实例数据进行更好的排序,有助于对齐填充产生的内存碎片
  • 对象的实例数据顺序,并不一定是编码顺序,这个和Hotspot的FieldsAllocationStyle取值有关系。
FieldsAllocationStyle取值

取值

含义

备注

0

基本数据类型的原始引用 > 填充字段 > 引用类型

填充的字节是为了对齐8字节填充的空白

1

引用类型 > 基本类型 > 填充字段

默认1

2

父类的引用和子类的引用放在一块,父类策略采用0,子类策略采用1,但是父子类的数据是有隔离的

如果一个项目的继承关系特别多,采用策略2,可以提高GC的销量,父子类的引用在一起,方便查找