Java对象的内存布局全流程

目录
  • 对象内存布局
  • 对象占用内存空间
  • 证明对象内存布局

开始先抛出一个问题:一个对象o,Object o = new Object();创建完成后会占用多少字节的内存?

要能回答这个问题,就需要了解java对象的内存布局。

对象内存布局

一个Java对象在内存中包括对象头、实例数据和对齐填充三个部分。如下图所示:

对象头

  • Mark Word:包含一系列的标记位比如hashcode、GC分代年龄、偏向锁位,锁标志位等。这个Mark Word在对象被加了不同量级的锁时所包含的内容和布局都有所不同,这涉及到锁升级的知识,暂不展开讨论
  • Klass Pointer:是一个指针,指向描述这个对象类型的元对象,例如Object.class,User.class等

实例数据

  • instance data:描述成员变量的信息,如果成员变量是引用类型,那么它就是一个指针。instance data的大小是所有成员变量的占用空间(基本数据类型大小+指针大小)

对齐

  • padding:在java中,为了能够更加高效的利用内存空间,会将对象大小设定为8bytes的整数倍,如果对象头+实例数据的大小不是8bytes的倍数,那么会在padding区域填充几个字节,使得对象占用空间是8bytes的倍数

那么对象布局中各个部分占用内存空间到底多大呢?

对象占用内存空间

由于目前64位操作系统已经基本普及,下面只分析64位操作系统下的情况

指针压缩

在64位系统中,一个指针占64 bits也就是8 bytes,而在32位系统中指针只占4个字节,于是为了能够减少内存消耗,从JDK1.6开始,JVM会默认支持指针压缩,会将指针大小压缩成4个字节,

这涉及到两个参数-XX:+UseCompressedOops,-XX:+UseCompressedClassPointers。

  • UseCompressedOops:oops: ordinary object pointer,普通对象指针压缩,例如Object o = new Object();其中o就是个指向new Object()对象的指针,o在指针压缩前占用8个字节,在指针压缩后占用4个字节
  • UseCompressedClassPointers:压缩Klass Pointer,压缩前8个字节,压缩后4个字节

对象头

Mark Word占8个字节

  • Klass Pointer:开启(默认)压缩4个字节,不开启压缩8个字节

实例数据

  • instance data:根据实际情况计算:如果成员变量是基本数据类型,那么占用空间就是基本数据类型的大小,Java的8大基本数据类型的大小如下:
数据类型 占用空间bytes
byte 1
short 2
int 4
long 8
float 4
double 8
char 2
boolean 1

如果成员变量是引用类型,那么就是一个指针大小(开启指针压缩占4字节,不开启指针压缩8字节)

对齐

  • padding:如果对象头+实例数据的大小不是8 bytes的倍数,那么就填充这个区域,使得对象占用空间能被8个字节整除(最小情况)

口说无凭,下面将会通过实验证明

证明对象内存布局

我们需要引用一个依赖:openjdk提供的jol-core:

<dependency>
	<groupId>org.openjdk.jol</groupId>
	<artifactId>jol-core</artifactId>
	<version>0.9</version>
</dependency>

1.查看默认情况下没有成员变量的对象布局

示例代码:

public class TestObj {
    public static void main(String[] args) {
        // 创建对象
        Object o = new Object();
        // 获得对象布局内容
        String s = ClassLayout.parseInstance(o).toPrintable();
        // 打印对象布局
        System.out.println(s);
    }
}

输出结果:

其中对象头(object header)有三个,前两个是Mark Word一共8个字节,后面一个是Klass Pointer,占4个字节,由于没有成员变量,所以实例数据没有占用空间,而最后4个字节描述信息为:loss due to the next object alignment,意思就是为了与下一个对象对齐而丢失的部分,也就是对齐填充空间

2.证明Klass Pointer在不开启压缩的情况下占用8个字节

我们只需要在jvm参数上加上-XX:-UseCompressedClassPointers即可,在IDEA工具中可以设置启动参数:

还是运行上述代码,运行程序结果:

如上图所示,对象头已经占用16个字节,前8个字节是Mark Word,后8个字节就是未压缩的Klass Pointer。我们还注意到对齐填充也没有了,原因是此时对象占用空间16个字节已经是8bytes的倍数,所以不需要填充,这完全印证了前面的分析

3.证明实例数据的存在以及大小

示例代码:

public class TestObj {
    public static void main(String[] args) {
        // 创建对象
        User user = new User(1, "zhangsan");
        // 获得对象布局内容
        String s = ClassLayout.parseInstance(user).toPrintable();
        // 打印对象布局
        System.out.println(s);
    }
}
class User {
    private int id;
    private String name;
    public User(int id, String name) {
        this.id = id;
        this.name = name;
    }
}

打印结果:

如上图所示,int类型的id占用4个字节,指向字符串对象的name指针占用4个字节,加上对齐,对象一共占用24 bytes

4.最后验证不开启指针压缩的情况下指针占用8 bytes

只需在jvm参数上加上-XX:-UseCompressedOops:

还是运行上面的代码,打印结果:

很显然,此时name指针已经占用了8个字节

一般来说,UseCompressedClassPointers和UseCompressedOops是默认开启的,我们无需关心也无需修改。但是有个隐藏的细节就是:UseCompressedClassPointers的开启依赖UseCompressedOops的开启,并且开启UseCompressedOops 也默认强制开启UseCompressedClassPointers,关闭UseCompressedOops 默认关闭UseCompressedClassPointers。

至此,关于java对象的内存布局已经有了一个基本的了解,那么文章一开始的问题现在应该能很轻松的回答:16个字节。

以上为个人经验,希望能给大家一个参考,也希望大家多多支持我们。

(0)

相关推荐

  • 详解Java对象创建的过程及内存布局

    一.对象的内存布局 对象头 对象头主要保存对象自身的运行时数据和用于指定该对象属于哪个类的类型指针. 实例数据 保存对象的有效数据,例如对象的字段信息,其中包括从父类继承下来的. 对齐填充 对齐填充不是必须存在的,没有特别的含义,只起到一个占位符的作用. 二.对象的创建过程 实例化一个类的对象的过程是一个典型的递归过程. 在准备实例化一个类的对象前,首先准备实例化该类的父类,如果该类的父类还有父类,那么准备实例化该类的父类的父类,依次递归直到递归到Object类. 此时,首先实例化Object类

  • 深入理解JVM之Java对象的创建、内存布局、访问定位详解

    本文实例讲述了深入理解JVM之Java对象的创建.内存布局.访问定位.分享给大家供大家参考,具体如下: 对象的创建 一个简单的创建对象语句Clazz instance = new Clazz();包含的主要过程包括了类加载检查.对象分配内存.并发处理.内存空间初始化.对象设置.执行ini方法等. 主要流程如下: 1. 类加载检查 JVM遇到一条new指令时,首先检查这个指令的参数是否能在常量池中定位到一个类的符号引用,并且检查这个符号引用代表的类是否已被加载.解析和初始化过.如果没有,那必须先执

  • 详解Java对象的内存布局

    前言 今天来讲些抽象的东西 -- 对象头,因为我在学习的过程中发现很多地方都关联到了对象头的知识点,例如JDK中的 synchronized锁优化 和 JVM 中对象年龄升级等等.要深入理解这些知识的原理,了解对象头的概念很有必要,而且可以为后面分享 synchronized 原理和 JVM 知识的时候做准备. 对象内存构成 Java 中通过 new 关键字创建一个类的实例对象,对象存于内存的堆中并给其分配一个内存地址,那么是否想过如下这些问题: 这个实例对象是以怎样的形态存在内存中的? 一个O

  • Java对象的内存布局全流程

    目录 对象内存布局 对象占用内存空间 证明对象内存布局 开始先抛出一个问题:一个对象o,Object o = new Object();创建完成后会占用多少字节的内存? 要能回答这个问题,就需要了解java对象的内存布局. 对象内存布局 一个Java对象在内存中包括对象头.实例数据和对齐填充三个部分.如下图所示: 对象头 Mark Word:包含一系列的标记位比如hashcode.GC分代年龄.偏向锁位,锁标志位等.这个Mark Word在对象被加了不同量级的锁时所包含的内容和布局都有所不同,这

  • Java对象在内存中的布局是如何实现的?

    1.-XX:FieldsAllocationStyle 对象在内存中的布局首要相关配置就是FieldsAllocationStyle,这个配置有3个可选值,即0.1.2.当值为2的时候,会经过一些逻辑判断最终转化为0或者1. -XX:FieldsAllocationStyle=0 表示先分配对象,然后再按照double/long.ints.chars/shorts.bytes/booleans的顺序分配其他字段,也就是类中声明的相同宽度的字段总是会被分配在一起,而相同宽度字段的顺序则是它们在cl

  • 浅谈C++中派生类对象的内存布局

    主要从三个方面来讲: 1 单一继承 2 多重继承 3 虚拟继承 1 单一继承 (1)派生类完全拥有基类的内存布局,并保证其完整性. 派生类可以看作是完整的基类的Object再加上派生类自己的Object.如果基类中没有虚成员函数,那么派生类与具有相同功能的非派生类将不带来任何性能上的差异.另外,一定要保证基类的完整性.实际内存布局由编译器自己决定,VS里,把虚指针放在最前边,接着是基类的Object,最后是派生类自己的object.举个栗子: class A { int b; char c; }

  • Java对象创建内存案例解析

    Java对象创建内存图解析 1. 栈 Java栈的区域很小 , 特点是存取的速度特别快,栈存储的特点是, 先进后出,存储速度快的原因: 栈内存, 通过 栈指针'来创建空间与释放空间,指针向下移动, 会创建新的内存, 向上移动, 会释放这些内存.这种方式速度特别快 , 仅次于PC寄存器,但是这种移动的方式, 必须要明确移动的大小与范围 ,明确大小与范围是为了方便指针的移动 , 这是一个对于数据存储的限制, 存储的数据大小是固定的 , 影响了程序的灵活性. 所以我们把更大部分的数据 存储到了堆内存中

  • JSP+Servlet制作Java Web登录功能的全流程解析

    0.新建web项目 首先,在MyEclipse里新建java web项目,项目名为login.此时,项目中包含一个src文件夹和一个WebRoot文件夹,以及Java Web自带的JRE库文件夹和J2EE库文件夹.其中,在WebRoot文件夹下,包含了WEB-INF文件夹和一个index.jsp页面文件. 接下来,新建一个JSP页面,命名为login.jsp. 项目文件的结构如下: 1.设计login.jsp页面 打开login.jsp页面后,修改第一行的代码为pageEncoding="utf

  • Java 对象在 JVM 中的内存布局超详细解说

    目录 一.new 对象的几种说法 二.Java 对象在内存中的存在形式 1. 栈帧(Frame) 2. 对象在内存中的存在形式 ① 3. 对象中的方法存储在那儿? 4. Java 对象在内存中的存在形式 ② 三.类中属性详细说明 四.细小知识点 1. 如何创建对象 2. 如何访问属性 五.Exercise 六.总结 一.new 对象的几种说法 初学 Java 面向对象的时候,实例化对象的说法有很多种,我老是被这些说法给弄晕. public class Test { public static v

  • 新手初学Java对象内存构成

    目录 Java对象内存构成 对象内存构成 对象头 Mark Word Klass Pointer# 实例数据 对齐数据 Talk is cheap, show me code 普通对象# 数组对象 总结 Java对象内存构成 今天来讲些抽象的东西 -- 对象头,因为我在学习的过程中发现很多地方都关联到了对象头的知识点,例如JDK中的 synchronized锁优化 和 JVM 中对象年龄升级等等.要深入理解这些知识的原理,了解对象头的概念很有必要,而且可以为后面分享 synchronized 原

  • java各种类型对象占用内存情况分析

    前言 其实一般的程序猿根本不用了解这么深,只有当你到了一定层次,需要了解jvm内部运行机制,或者高并发多线程下,你写的代码对内存有影响,你想做性能优化.等等等等,一句话,当你想深入了解java对象在内存中,如何存储,或者每个对象占用多大空间时,你会感谢这篇文章 本文主要分析jvm中的情况,实验环境为64位window10系统.JDK1.8,使用JProfiler进行结论验证 很多描述以及 概念是基于你懂基本java知识的,如果你看起来有点吃力,要加油咯 本片更偏重验证,更多理论,请参考:http

随机推荐