Java 基础(4)—Java 对象布局及偏向锁、轻量锁、重量锁介绍
一、Java 对象内存布局
1、对象内存布局
一个对象在 Java 底层布局(右半部分是数组连续的地址空间),如下图示:
总共有三部分总成:
1. 对象头:储对象的元数据,如哈希码、GC 分代年龄、锁状态标志、线程持有的锁等等。
2. 实例数据:存储对象实际的数据内容,即程序员定义的各种类型的变量。
3. 对其填充:为了 JVM 能够更快地访问对象内部的数据,会在实例数据后面填充额外的空间,使得对象的大小能够被虚拟机的内存管理系统所整除(一般都是8的倍数)。
具体对象头的大小和实例数据的大小,与 Java 虚拟机的具体实现、对象的类型、虚拟机运行时参数等都有关系,一般不是固定的数值。需要注意的是,数组对象与普通对象的内存布局是不一样的,数组对象会额外存储数组长度信息。
1.1、对象头
点击进入 openjdk 官网
1.1.1、mark word 标记字
对象头两部分组成:
1. 对象标记(mark word):储对象的元数据,如哈希码、GC 分代年龄、锁状态标志、线程持有的锁
2. 类元信息(klass pointer):存储的是指向该对象类元数据(klass)所在的首地址。
在 64位操作系统上,Markword 占了8个字节,类型指针占了8个字节,一共占16个字节。也就是说你随便 new 一个对象对象头就直接占了 16个字节(但是不一定,可能会压缩类型指针)。
1.1.2、klass pointer 类型指针
可以参考下图,类型指针指向方法区,比如有个 Customer 类,new 一个 Customer 实例,这个实例的类型指针指向方法区中的 Customer 类元信息。
1.2、实例数据
存放类的 Field 数据信息,包括父类的属性信息;如果是数组实例部分,还需要包括数组的长度,这部分内存按照4个字节对齐。
举个例子如下:
public class MarkwordDemo {
public static void main(String[] args) {
new Apple();
}
}
class Apple {
}
直接 new 一个空属性 Apple 实例,在内存中就已经占用16字节(不考虑类型指针压缩),如果 Apple 类中还有其他属性呢?如下所示:
public class MarkwordDemo {
public static void main(String[] args) {
new Apple();
}
}
class Apple {
int size = 100;
char a = 'a';
}
一个 int
类型占 4个字节,一个 char
字符占1个字节,所以 new 一个 Apple 实例就会占用 16+5 = 21 个字节,但是最终会占用24个字节,因为 Java 底层为了方便内存管理,需要将其对齐填充,并且一般是8的倍数,所以是24字节。
1.3、对其填充
虚拟机要求对象起始地址必须是8字节的整数倍,填充数据不是必须存在的,仅仅是为了字节对齐,这部分内存按照8字节补充对齐。
推荐阅读文章
-
由 Spring 静态注入引发的一个线上T0级别事故(真的以后得避坑)
-
如何理解 HTTP 是无状态的,以及它与 Cookie 和 Session 之间的联系
-
HTTP、HTTPS、Cookie 和 Session 之间的关系
-
什么是 Cookie?简单介绍与使用方法
-
什么是 Session?如何应用?
-
使用 Spring 框架构建 MVC 应用程序:初学者教程
-
有缺陷的 Java 代码:Java 开发人员最常犯的 10 大错误
-
如何理解应用 Java 多线程与并发编程?
-
把握Java泛型的艺术:协变、逆变与不可变性一网打尽
-
Java Spring 中常用的 @PostConstruct 注解使用总结
-
如何理解线程安全这个概念?
-
理解 Java 桥接方法
-
Spring 整合嵌入式 Tomcat 容器
-
Tomcat 如何加载 SpringMVC 组件
-
“在什么情况下类需要实现 Serializable,什么情况下又不需要(一)?”
-
“避免序列化灾难:掌握实现 Serializable 的真相!(二)”
-
如何自定义一个自己的 Spring Boot Starter 组件(从入门到实践)
-
解密 Redis:如何通过 IO 多路复用征服高并发挑战!
-
线程 vs 虚拟线程:深入理解及区别
-
深度解读 JDK 8、JDK 11、JDK 17 和 JDK 21 的区别
-
10大程序员提升代码优雅度的必杀技,瞬间让你成为团队宠儿!
-
“打破重复代码的魔咒:使用 Function 接口在 Java 8 中实现优雅重构!”
-
Java 中消除 If-else 技巧总结
-
线程池的核心参数配置(仅供参考)
-
【人工智能】聊聊Transformer,深度学习的一股清流(13)
-
Java 枚举的几个常用技巧,你可以试着用用
-
由 Spring 静态注入引发的一个线上T0级别事故(真的以后得避坑)
-
如何理解 HTTP 是无状态的,以及它与 Cookie 和 Session 之间的联系
-
HTTP、HTTPS、Cookie 和 Session 之间的关系
-
使用 Spring 框架构建 MVC 应用程序:初学者教程
-
有缺陷的 Java 代码:Java 开发人员最常犯的 10 大错误
-
Java Spring 中常用的 @PostConstruct 注解使用总结
-
线程 vs 虚拟线程:深入理解及区别
-
深度解读 JDK 8、JDK 11、JDK 17 和 JDK 21 的区别
-
10大程序员提升代码优雅度的必杀技,瞬间让你成为团队宠儿!
-
探索 Lombok 的 @Builder 和 @SuperBuilder:避坑指南(一)
-
为什么用了 @Builder 反而报错?深入理解 Lombok 的“暗坑”与解决方案(二)