Java篇之对象

1. 对象

一个对象包含的信息有:对象头,对象实例数据、对齐填充

在这里插入图片描述
(图片来源于网络)

在这里插入图片描述

2. 怎么创建对象

创建对象,要完成的是在内存给对象分配到一块空间,并设置相应的信息。创建对象需要以下5步:

在这里插入图片描述

Step1:类加载检查

检查这个符号引用代表的类是否已被加载过、解析和初始化过。如果没有,那必须先执行相应的类加载过程。

Step2:分配内存

类加载之后,虚拟机将为新生对象分配内存。对象所需的内存大小在类加载完成后便可确定,为对象分配空间的任务等同于把一块确定大小的内存从Java堆中划分出来。

Step3:初始化零值

内存分配完成后,虚拟机需要将分配到的内存空间都初始化为零值(不包括对象头),这一步操作保证了对象的实例字段在Java代码中可以不赋初始值就直接使用,程序能访问到这些字段的数据类型所对应的零值。

Step4:设置对象头

初始化零值完成之后,虚拟机要对对象进行必要的设置,例如这个对象是哪个类的实例、如何才能找到类的元数据信息、对象的哈希码、对象的 GC 分代年龄等信息。 这些信息存放在对象头中。 另外,根据虚拟机当前运行状态的不同,如是否启用偏向锁等,对象头会有不同的设置方式。

Step5:执行 init 方法

在上面工作都完成之后,从虚拟机的视角来看,一个新的对象已经产生了,但从Java程序的视角来看,对象创建才刚开始,<init> 方法还没有执行,所有的字段都还为零。所以一般来说,执行 new 指令之后会接着执行 <init> 方法,把对象按照程序员的意愿进行初始化,这样一个真正可用的对象才算完全产生出来。

3. 内存分配方式 - 怎么给对象分配内存

分配方式“指针碰撞”“空闲列表” 两种,选择哪种分配方式由 Java堆是否规整决定,而 Java 堆是否规整又由所采用的垃圾收集器是否带有压缩整理功能决定。

3.1 指针碰撞

用过的内存全部整合到一边,没有用过的内存放在另一边,中间有一个分界值指针,只需要向着没用过的内存方向将该指针移动对象内存大小位置即可。

在这里插入图片描述

(图片来源于网络)

3.2 空闲列表

虚拟机会维护一个列表,该列表中会记录哪些内存块是可用的。在分配的时候,找块儿足够大的内存块儿来划分给对象实例,最后更新列表记录。

在这里插入图片描述

(图片来源于网络)

4. 对象访问定位 - 怎么找到对象

主流的访问方式有使用句柄直接指针两种。

4.1 使用句柄

Java堆中将会划分出一块内存来作为句柄池,reference中存储的就是对象的句柄地址,而句柄中包含了对象实例数据与类型数据各自的具体地址信息;

(图片来源于网络)

4.2 直接指针

Java堆对象的布局中就必须考虑如何放置访问类型数据的相关信息,而reference中存储的直接就是对象的地址。

在这里插入图片描述

(图片来源于网络)

5. 如何判断对象不可用

5.1 引用计数法

给对象中添加一个引用计数器,每当有一个地方引用它,计数器就加1;当引用失效,计数器就减1;任何时候计数器为0的对象就是不可能再被使用的。

对象无法回收的情况:

如果有两个对象相互引用,那么它们的引用计数器都不为0,垃圾回收器无法回收它们。

5.2 可达性分析

GC Root 作为起点,向下搜索,遍历可以到达的节点。节点走过的路径是引用链,如果一个对象没有引用链相连,那么就说明这个对象是不可用的。

GC Root 的特点是 当前存活的对象。它需要保证引用所指向的对象都是活着的,当前线程栈帧中的对象在当前时刻肯定是活着的。

可以作为 GC Root 的有(栈、方法区、同步锁持有的对象):

  • 虚拟机栈(栈帧中的本地变量表)中引用的对象。

  • 本地方法栈(Native方法)中引用的对象

  • 方法区中类静态属性引用的对象 static

  • 方法区中常量引用的对象。String s = "abc"

  • 所有被同步锁持有的对象。对象的方法中有加锁(用synchronized修饰)的方法

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值