导航
- 一、对象的创建过程
- 二、对象的内存布局
- 2.1 内存布局
- 2.2 计算对象的内存大小
- 三、对象的定位
- 3.1 句柄池
- 3.2 直接指针
- 四、对象的分配过程
一、对象的创建过程
对象,又叫实例,是 OOP 的最常用角色。
如何创建一个对象?一般都是使用 new 关键字搭配对象的构造函数:
// 基类的创建
Object obj = new Object();
// 普通用户对象的创建
User user = new User("Tom", 29);
// 服务类对象的创建
Service svic = new ServiceImpl(user);
上述代码创建了三个对象:obj、user、svic。
这个过程大致可以分为以下几个步骤:
- 如果该对象的类还未使用过,需要加载类的信息,并完成连接、初始化等操作。参考《JVM——详解类加载过程》
- 申请对象,内存成员变量赋 default value,此步骤和类加载过程的 preparation 同理。
- 调用构造方法<init> : 成员变量顺序赋初始值,执行构造方法语句
- 最后将实例数据的指针返回,存储在位于栈中的引用数据中,即引用赋值。
二、对象的内存布局
2.1 内存布局
在JVM 中,对象的内存布局分为:
- 对象头(Header),Mark Word + Class Pointer。
- 实例数据(Instance Data),包含父类所有字段信息。
- 对齐填充(Padding),任何对象大小都必须是 8 字节的整数倍。
对象头包含两类数据:
- Mark Word :它是对象自身的运行时数据,在 Hotspot 虚拟机中,该数据长度是 8 字节(64位操作系统)。包括哈希码、GC分代年龄、锁状态标志、偏向线程ID、偏向时间戳等。
- Class Pointer :类型指针,–XX:+UseCompressedClassPointers 压缩类型指针,开启时该数据为 4字节,不开启为 8 字节。
2.2 计算对象的内存大小
Java 语言并不具备类似 C 语言中的 sizeOf 函数,可以直接获取对象的大小。但是,Java 提供了一个 agent 机制,可以通过这个机制来计算 Java 对象的内存大小。
Agent 机制简单的理解就是,一个 class 要加载到内存,JVM 可以提供一个 Agent 代理来截获这些 class 文件,以此来读出整个Object 大小。
三、对象的定位
对象的定位指的是,引用是如何找到具体的实例数据地址的?
目前有两种实现方式:句柄池、直接指针。
两种方式各有优劣,句柄池的方式更安全,而直接指针更快。HotSpot 虚拟机是采用直接指针的方式来实现对象定位的。
3.1 句柄池
3.2 直接指针
四、对象的分配过程
首先,new 一个对象的时候,先往栈上分配,如果能分配的下,就直接分配在栈上,然后栈一弹出对象就没了。如果栈上分配不下,特别大的话,直接分配到堆内存老年代。如果不大,首先会进行线程本地分配,如果能分配的下,就直接分配,如果分配不下,找 eden 区。