前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
社区首页 >专栏 >承前启后,Java对象内存布局和对象头

承前启后,Java对象内存布局和对象头

原创
作者头像
小高先生
发布于 2024-02-21 13:27:02
发布于 2024-02-21 13:27:02
20901
代码可运行
举报
运行总次数:1
代码可运行

承前启后,Java对象内存布局和对象头

大家好,我是小高先生。在我之前的一篇文章《并发编程防御装-锁(基础版)》中,我简要介绍了锁的基础知识,并解释了为什么Java中的任何对象都可以作为锁。在那里,我提到了对象头中有一个指向ObjectMonitor的指针,但没有深入探讨Java对象的内存结构。本文将引导大家深入了解Java对象的内存布局以及对象头结构,帮助大家更好地理解Java中的对象和锁,并为之后学习synchronized和锁升级打下基础。

  • new Object()怎么理解?
  • JOL
  • GC分代年龄

new Object()怎么理解?

平时我们开发的时候经常要new一个对象,很少想过这个对象存在哪里以及这个对象是有什么构成的,所以关于new Object的理解可归于两点:

1.位置所在(where)

学过JVM的同学都应该知道JVM内存结构,我们new出来的对象就在堆区,大对象在老年代,小对象在新生代。

2.构成布局(what)

1.对齐填充

先讲一下对齐填充,这个比较简单。HotSpot虚拟机的自动内存管理系统要求对象的起始地址必须是8字节的整数倍,也就是对象的大小是8字节的整数倍。对象头已经被设计成8字节的倍数,通常是1倍或2倍,如果实例数据大小不是8字节的整数倍,就需要用对齐填充添加一下,使对象大小为8字节的整数倍。

2.对象头

对象头是对象的另一个重要组成部分,它包含了一些关于对象的元信息。具体来说,对象头包括Mark Word和类元信息(类型指针)。

Mark Word

平时写代码的时候可能会通过hashcode()方法获取对象的哈希码,那哈希码保存在哪里呢?还有JVM中垃圾回收机制里说过,当一个对象在新生代经过15次Minor GC之后还存活下来就会进入老年代,那对象怎么记录GC次数呢?这些信息都记录在Mark Word中,先不需要都知道里面的标记代表什么,后续会讲到,这里只需要知道Mark Word存储了对象的一些重要信息。

存储内容

标志位

状态

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

01

未锁定

指向锁记录的指针

00

轻量级锁定

指向重量级锁的指针

10

重量级锁定

空,不需要记录信息

11

GC标记

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

01

可偏向

在64位系统中,Mark Word占8字节,类型指针占8字节,一共16字节。所以当我们new一个对象,还没有实例数据,那这个对象就是16字节。64为系统下,Mark Word就是64bit大小,存储结构如下:

比如现在对象是无锁状态,那Mark Word前25bit不用,之后的31bit存hashCode,偏向锁标志位为1,锁标志位为01,其他锁状态会在相关文章中讲解。

Mark Word默认存储hashCode、分代年龄和锁标志位等相关信息,这些信息都是与对象自身定义无关的数据,会根据对象的状态复用自己的存储空间,运行期间数据会随着锁状态而改变

类元信息(类型指针)

Phone ph = new Phone(),创建一个对象,会在JVM方法区存放对象的Klass类元信息,对象头的类型指针就是这个对象指向它的类元信息的指针,JVM通过类型指针确定这个对象是哪个类的实例,比如我创建Animal类,new Animal()实例的类型指针就会指向方法区中的Animal的Klass类元信息。由于Animal里面没有创建属性,所以Animal对象还没有实例数据,大小为16字节。

代码语言:java
AI代码解释
复制
public class ObjectHeadDemo {
    public static void main(String[] args) {
        Animal a = new Animal();
    }
}
class Animal{

}

我给大家百度了Klass类元信息具体是什么,因为一开始我也不懂。

Klass类元信息是JVM中表示类的元信息的数据结构

JVM中,每个Java类都对应一个Klass实例,是用C++编写的一个类,存储了Java类的所有元信息,包括属性、方法、修饰符等。Klass模型是JVM内部使用的一个概念,是Java类的内部表示。这个模型包含了类的所有信息,如类的完整名称、访问修饰符、父类、实现的接口、属性和方法等。这些信息在类加载的过程中被读取并存储到方法区。

这么看起来,Klass就想是这个类的模板。

3.实例数据

代码语言:java
AI代码解释
复制
class Animal{
	int id;
    boolean flag = false;
}

如果创建了一个Animal对象,大小就是对象头(16字节)+ int(4字节)+ boolean(1字节)= 21字节,需要有对齐填充,21 + 3 = 24字节。

JOL

有关Java对象布局的理论知识已经学完了,那能不能从代码层面验证一下对象的结构呢。

JOL(Java Object Layout)是一个专门用于分析Java虚拟机(JVM)中对象内存布局的工具箱。它能够提供非常精确的关于对象如何分布在JVM内存中的信息。

1.先在POM文件中导入坐标

代码语言:java
AI代码解释
复制
<dependency>
    <groupId>org.openjdk.jol</groupId>
    <artifactId>jol-core</artifactId>
    <version>0.17</version>
</dependency>

2.通过指令查看JVM详细细节

代码语言:java
AI代码解释
复制
public class JOLDemo {
    public static void main(String[] args) {
        //VM的详细细节
        System.out.println(VM.current().details());
        //获取当前JVM实例的对象对齐方式
        System.out.println(VM.current().objectAlignment());
       
    }
}
class Customer{
    int id;
    boolean flag = false;
}

Object alignment指的是对象在内存中的对齐方式。8 bytes表示对象的大小必须是8的倍数,即对象的起始地址必须是8的整数倍。

我们创建一个Object类,看看这个类具体的结构是什么样的,通过ClassLayout.parseInstance(o).toPrintable()来查看。

代码语言:java
AI代码解释
复制
public class JOLDemo {
    public static void main(String[] args) {
        Object o = new Object();
        System.out.println(ClassLayout.parseInstance(o).toPrintable());
    }
}

参数

作用

OFFSET

偏移量,也就是这个字段起始位置

SIZE

字节大小

TYPE

Class中定义的类型

DESCRIPTION

类型描述

VALUE

是TYPE在内存中的值

我们这里会发现类型指针只占了4字节,之前说的是占8字节,这是为什么?这个我们稍后讨论。

另外一个细节会发现Instance size为16字节,因为对象头不够8的倍数,要补齐,也就是new一个对象默认大小为16字节。刚刚是创建了一个Object对象,现在再创建一个自己写的类看看结果,并且类中没有实例数据。结果表明自己写的类和Object类new出来的对象内存布局是一样的。

代码语言:java
AI代码解释
复制
public class JOLDemo {
    public static void main(String[] args) {
        Object o = new Object();
        //System.out.println(ClassLayout.parseInstance(o).toPrintable());
        
        Customer c = new Customer();
        System.out.println(ClassLayout.parseInstance(c).toPrintable());
    }
}
class Customer{
	//只有对象头,没有实例数据
}

另外一种情况是类中有实例数据,看看结果,这次除了对象头以外多了实例数据,然后也是用对齐填充补到8的倍数。

代码语言:java
AI代码解释
复制
public class JOLDemo {
    public static void main(String[] args) {
        Object o = new Object();
        //System.out.println(ClassLayout.parseInstance(o).toPrintable());

        Customer c = new Customer();
        System.out.println(ClassLayout.parseInstance(c).toPrintable());
    }
}
class Customer{
    int id;
    String name;
}

现在来研究研究为什么类型指针是4而不是8,这是因为压缩指针的影响。使用java -XX:+PrintCommandLineFlags -version指令,启动Java程序的时候把所有参数打印出来。-XX:+UseCompressedClassPointers这个参数就表示使用了压缩指针,默认开启,结果就导致类型指针是4字节。

试试不开启指针压缩是什么样,正常工作中是不会用到的,这里就是跟大家尝试一下,看看结果而已。

GC分代年龄

GC年龄在Mark Word中用4bit存储,最大为15,也就是JVM参数-XX:MaxTenuringThreshold = 15。我们也可以验证一下,调整参数改成16试试,运行时是有异常的。

总结

本文和朋友们一起学习Java对象内存布局的知识,对象由对象头、实例数据和对齐填充组成。对象头包括Mark Word和类型指针,Mark Word记录一些对象的关键信息,主要包括锁的状态,类型指针是可以确定这个对象是哪个类的实例。JVM要求对象大小是8字节的整数倍,所以当对象头和实例数据大小不是8字节整数倍的时候,就需要对齐填充帮忙补齐。

东西不多但是挺重要的,可以更好地理解之前讲的synchronized底层分析,也可以对未来要学的锁升级做好准备。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
暂无评论
推荐阅读
npm包管理器基本使用
NPM是随同NodeJS一起安装的包管理工具,能解决NodeJS代码部署上的很多问题,常见的使用场景有以下几种:
meowrain
2021/04/22
6240
npm包管理器基本使用
你真的知道 NPM、Yarn 与 PNPM 这三个前端包管理器之间的区别吗?
在当代的Web开发过程中,JavaScript项目的构建离不开各种外部依赖,无论是实用的库、辅助工具还是其他类型的资源。这些依赖项的管理,已经成为了开发者日常不可或缺的一部分。NPM、Yarn和PNPM这三个包管理器,就像是开发者的得力助手,它们在项目开发中扮演着至关重要的角色。本文将带你一探究竟,了解这些工具的魅力所在,并帮助你选择适合自己项目的包管理器。
前端达人
2024/04/12
4.7K0
你真的知道 NPM、Yarn 与 PNPM 这三个前端包管理器之间的区别吗?
Node.js 包管理器 NPM 讲解
包管理器又称软件包管理系统,它是在电脑中自动安装、配制、卸载和升级软件包的工具组合,在各种系统软件和应用软件的安装管理中均有广泛应用。对于我们业务开发也很受益,相同的东西不必重复去造轮子。
五月君
2019/08/13
1.5K0
使用 npm 管理nodejs包
npm 全称为 Node Package Manager,是一个基于 Node.js 的包管理器,也是 Node.js 社区最流行、支持的第三方模块最多的包管理器。它的初衷就是让开发人员更容易分享和重用代码。npm 提供了命令行工具,其主要功能是管理Node.js包,包括安装、更新、删除、查看、搜索、发布等。
全栈研发知识库
2024/11/05
2040
npm 与 package.json 快速入门
张拭心 shixinzhang
2018/01/05
2.1K0
npm 与 package.json 快速入门
Node.js学习笔记(一)——Node.js概要、NPM与package.json
Node.js 是一个基于Google Chrome V8 引擎的 JavaScript 运行环境。Node.js 使用了一个事件驱动、非阻塞式 I/O 的模型,使其轻量又高效。Node.js 的包管理器 npm,是全球最大的开源库生态系统。
张果
2022/09/28
2.6K0
Node.js学习笔记(一)——Node.js概要、NPM与package.json
如何发布npm包
1、npm是什么? NPM (node package manager),通常称为node包管理器。顾名思义,它的主要功能就是管理node包,包括:安装、卸载、更新、查看、搜索、发布等。 npm 可以让 JavaScript 开发者在共享代码、复用代码以及更新共享的代码上更加方便。 当一个 JavaScript 开发者为了解决某个问题而编写了一些代码并将其共享出来的话,其他的开发者能够在自己的应用程序中复用这些代码,npm 让这些事情变得简单。 如果你使用了其他开发者开发的代码,你就可以很方便地使用 npm
小胖
2018/06/27
1.4K0
yarn -- 新型包管理器
随着nodejs的出现,另外两个东西也进入了前端大众的视野–CommonJS规范、node 包管理器。
用户1394570
2018/08/08
6460
NPM基本介绍(一)
一、commonJS规范 在介绍npm的前面,我们先介绍一下commonJS规范。commonJS规范的提出,主要是为了弥补js没有标准的缺陷,已达到Ruby和Java具备开发大型应用的基础能力,如今,commonJS中的大部分规范虽然依旧是草案,但是已经为javascript开发大型应用指明了一条非常棒方向,目前,它依然在成长,目前commonJS规范涵盖了模块、二进制、Buffer、字符集编码、I/O流、进程环境、文件系统、套接字、单元测试、web服务器网关接口、包管理等。而其中的npm则就是commo
pitaojin
2018/05/25
1.6K0
适合前端小白的-NPM包管理命令合集
NPM最初是作为Node.js的软件包管理工具而创建的,用于管理Node.js应用程序的依赖关系和模块。随着时间的推移,NPM变得非常流行,并且逐渐成为广泛用于前端和后端JavaScript开发的工具。开发人员可以使用NPM来安装、更新、发布和管理各种JavaScript包(也称为模块或库),这些包可以包含从小型功能性工具到大型应用框架的各种类型的代码。
zayyo
2023/11/04
2830
Node.JS 学习记录(01)
64 位安装包下载地址 : https://nodejs.org/dist/v4.4.3/node-v4.4.3-x64.msi
王也518
2022/10/26
8450
npm详解
文章目录 npm 是什么? 安装 npm 更新 npm package.json 文件 package.json 如何创建 package.json 的内容 dependencies(生产环境)和 devDependencies(开发环境)的区别 指定安装包的版本 自定义 package.json 安装包文件 引用文件库 npm 是什么? npm(node package manager):node.js 的包管理器,用于node插件管理(包括安装、卸载、管理依赖等) ,npm 是随同 node.js 一
兮动人
2021/06/11
1.6K0
npm详解
零基础入门Node.js :从软件下载到Hello World完整教程
Node.js 是一个基于Chrome V8引擎的JavaScript运行时环境,采用事件驱动、非阻塞I/O模型,广泛应用于服务端开发、前端工具链构建等场景。其核心优势包括:
万里顾一诚
2025/03/17
4901
零基础入门Node.js :从软件下载到Hello World完整教程
JavaScript 包管理器
npm、yarn、pnpm 都是现代化的 JavaScript 包管理器,它们的异同如下:
yiyun
2023/05/18
1.1K0
JavaScript 包管理器
理解Node.js安装及模块化
中文网:http://nodejs.cn/ 英文网:https://nodejs.org
小周sir
2019/09/23
1K0
最高性能的包管理器-pnpm
每一个文件都有一个唯一的 inode,它包含文件的元信息,在访问文件时,对应的元信息会被 copy 到内存去实现文件的访问。
GopalFeng
2022/08/01
1.7K0
最高性能的包管理器-pnpm
前端基础-Node.js包管理器npm
上面的代码,我们使用npm安装了moment来进行格式化时间的处理,这就是使用第三方模块;
cwl_java
2020/03/26
1.3K0
前端包管理工具与配置项
任何一个项目的构建离不开工具和统一的管理标准,在项目开发和维护过程中,我们需要了解安装包的相应工具和配置文件,以此来有效的进行项目的迭代和版本的更新,为项目提供基本的运行环境。
程序员海军
2023/11/07
5660
【规范】统一项目中包管理器的使用
Dear,大家好,我是“前端小鑫同学”,😇长期从事前端开发,安卓开发,热衷技术,在编程路上越走越远~ 【规范】统一项目中包管理器的使用 背景介绍: 我们这里暂不说各种包管理器的优缺点,在实际开发中遇到的一个问题就是,你本地经常使用cnpm来安装,但Jenkins自动构建用的npm,偶尔就会出现本地开发很正常但是Jenkins构建失败报警了,为了避免类似问题的出现,也应该要将能统一的都统一规范。 实现原理: 通过preinstall来在执行install前执行指定脚本; 在preinstall脚
前端小鑫同学
2022/12/26
1.4K0
【规范】统一项目中包管理器的使用
包资源管理器NPM
NPM全称Node Package Manager,他是node包管理和分发工具。其实我们可以把NPM理解为前端的Maven .
名字是乱打的
2021/12/22
1K0
相关推荐
npm包管理器基本使用
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验