Java虚拟机--对象内存布局

对象中的数据

前两篇,我们讲到了Java对象的类加载Java对象的初始化操作。本篇,我们来继续学习Java对象,看看Java对象在内存中如何布局,看看Java对象中由哪些数据构成,以及教给大家如何测量一个对象的大小。

HotSpot虚拟机下,一个对象在内存中包含了3大区域,分别为:对象头(Header)、实例数据(Instance Data)和对齐填充(Padding)。

对象头(Header)

对象头,顾名思义就是对象的头部。如果按照一个团队来看,对象头就好比团队中的领导。对于一个团队来说,领导至关重要。对于一个对象来说,对象头不可或缺。

在计算机领域中,很多知识概念都有的存在,例如:Http请求头。

在HotSpot虚拟机中,对象头包括两部分:Mark Word类型指针

那么,什么是Mark Word呢?什么是类型指针呢?

(1)Mark Word

Mark Word直译为标记字段,主要用于存储对象自身的运行时数据,如哈希码(HashCode)、GC分代年龄、锁状态标志、线程持有的锁、偏向线程ID、偏向时间戳等。

在不同的运行环境中,所占用内存的大小也不尽相同。在32位的机器中,Mark Word大小为32bit,4字节。在64位机器中,Mark Word大小为64bit,也就是8字节。

考虑到虚拟机的存储空间,Mark Word被设计成一个非固定的数据结构以便在最小的内存中存储更多的有用信息。

对于Mark Work来说,在不同场景下存储着不一样的信息。例如:在32位计算机中,如果一个对象处于未锁定(没有被加锁)状态下,那么该对象的Mard Word大小为32bit,其中25bit用户存储对象哈希码,4bit用于存储对象分代年龄,2bit用于存储锁标志位,1bit固定为0。

而其他场景如下:

场景

存储内容

锁标志位

未加锁

对象哈希码、对象分代年龄

01

轻量级锁定

指向锁记录的指针

00

重量级锁定

指向重量级锁的指针

10

GC标记

不需要记录任何信息

11

偏向锁

偏向线程ID、偏向时间戳、对象分代年龄

01

可以看出,在不同场景下,Mark Word中存储的数据是不一样的。要注意的是,当一个对象从未加锁状态转向其他状态时,原有的存储内容并不会丢失、被覆盖,而是在当前线程的栈帧中建立一个名为锁记录(Lock Record)的空间,用于存储锁对象目前的Mark Word的拷贝。

对于想要了解锁机制的朋友,首先需要对Mark Word有所了解,锁的实现离不开Mark Word。关于锁的内容,会在后面的多线程中进行讲解。

(2)类型指针

类型指针:对象指向其类元数据的指针,虚拟机通过这个指针确定该对象是哪个类的实例。

值得注意的是,如果对象是一个Java数组,那在对象头中还必须有一块区域用于记录数组的长度,因为虚拟机可以通过普通Java对象的元数据信息确定Java对象的大小,但是从数组的元数据中无法确定数组的大小。

实例数据(Instance Data)

实例数据部分,存储着对象真正的有效信息,也就是代码中定义的各种类型字段内容。无论是父类继承下来的,还是子类中自己定义的,都会在实例数据中保存起来。

实例数据的保存顺序,跟代码中定义的顺序有较大的关系,通常情况下父类中定义的变量会排在子类之前。

原生类型(primitive type)的内存占用如下:

原生类型

占用内存大小(字节)

boolean

1

byte

1

short

2

char

2

int

4

float

4

long

8

double

8

reference

4/8

对齐填充(Padding)

对齐填充,主要起到补全的作用。在HotSpot VM中,要求对象的大小必须是8字节的整数倍,当对象头+实例数据的大小不满足此要求时,就用到对齐填充来进行补全

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏恰童鞋骚年

【译】.NET中六个重要的概念:栈、堆、值类型、引用类型、装箱和拆箱

  一来是为了感受国外优秀技术社区知名博主的高质量文章,二来是为了复习对.NET技术的基础拾遗达到温故知新的效果,最后也是为了锻炼一下自己的英文读写能力。因为是...

752
来自专栏Java爬坑系列

【JAVA零基础入门系列】Day5 Java中的运算符

  运算符,顾名思义就是用于运算的符号,比如最简单的+-*/,这些运算符可以用来进行数学运算,举个最简单的栗子:   已知长方形的长为3cm,高为4cm,求长方...

2097
来自专栏逍遥剑客的游戏开发

Nebula3学习笔记(3): 核心库

19811
来自专栏java一日一条

10个惊艳的Swift单行代码

几年前,一篇表述“10个Scala函数式单行代码”的文章非常受欢迎,并且随后立马出现了其他的语言版本,例如Haskell版本,Ruby版本,Groovy版本,C...

962
来自专栏项勇

笔记45 | 代码性能优化建议[转]

1396
来自专栏程序你好

最近我遇到的10个Java面试问题

最近,我参加了一些java的面试。突然,我有了一个想法,我想和大家分享我的经历。我希望我能通过分享我最近几个月遇到的10个Java面试问题来帮助大家。

963
来自专栏机器之心

教程 | PyTorch内部机制解析:如何通过PyTorch实现Tensor

选自Github 机器之心编译 参与:朱乾树、黄小天 PyTorch 中的基本单位是张量(Tensor)。本文的主旨是如何在 PyTorch 中实现 Tens...

4245
来自专栏Java学习网

Java面试题系列之基础部分(六)——每天学5个问题

Java基础部分学习的顺序:基本语法,类相关的语法,内部类的语法,继承相关的语法,异常的语法,线程的语法,集合的语法,io的语法,虚拟机方面的语法,这些都是最基...

2665
来自专栏java达人

Java中的堆和栈的区别

当一个人开始学习Java或者其他编程语言的时候,会接触到堆和栈,由于一开始没有明确清晰的说明解释,很多人会产生很多疑问,什么是堆,什么是栈,堆和栈有什么区别?更...

2366
来自专栏大史住在大前端

野生前端的数据结构基础练习(2)——队列

循环队列书中并没有提及,它是一种特殊的队列。简单理解就是将基本队列只当做存储结构,而使用front和rear两个指针分别代表队列的头和尾,实际对外表现的队列是f...

2203

扫码关注云+社区

领取腾讯云代金券