JVM(六)Java中对象的实例化

93 阅读4分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第18天,点击查看活动详情

前言

首先先看一下这三道,大脑回忆一下是否能够回答,如果不能的话,浏览下面的内容吧。

  • 对象在VM中是怎么存储的?
  • 对象头信息里面有哪些东西?
  • Java创建实例对象是不是必须要通过构造函数?

创建对象的方式

一般来说,大多数人最为熟悉的就是new 一个对象,但实际上,有五种方式:

1.new关键字

Phone phone = new Phone();

这是我们最常见的也是最简单的创建对象的方式,通过这种方式我们还可以调用任意的构造器(无参的和有参的)。

2.Class.newInstance

反射创建对象时最常用的方法,调用的是类的无参构造方法,因此必须保证类声明了有参构造后同时声明一个无参构造。

Phone phone1 = Phone.class.newInstance();

3.Constructor.newInstance

Person person = constructor.newInstance("luffy","20");

我们可以通过这个newInstance方法调用有参数(不再必须是无参)的和私有的构造函数(不再必须是public)。

4.Clone方式

package com.hong.com.hong.juejin;

/**
 * @author hong
 * @date 2022年10月28日 14:51
 */
public class Phone implements Cloneable{

    private String brand;
    private float price;

    public Phone(String brand,float price){
        this.brand = brand;
        this.price = price;
    }
    public Phone clone() throws CloneNotSupportedException {
        return (Phone) super.clone();
    }

    @Override
    public String toString() {
        return "Phone{" +
                "brand='" + brand + ''' +
                ", price=" + price +
                '}';
    }
}
public static void main(String[] args) throws CloneNotSupportedException {
    Phone phone = new Phone("mi",1999);
    Object clone = phone.clone();

    System.out.println(phone);
    System.out.println(clone);

}

Snipaste_2022-10-28_15-13-03.jpg 要使用clone方法,我们必须先实现Cloneable接口并复写Object的clone方法。调用一个对象的clone方法,JVM就会创建一个新的对象,将前面的对象的内容全部拷贝进去,用clone方法创建对象并不会调用任何构造函数

5.反序列化

当我们序列化和反序列化一个对象,JVM会给我们创建一个单独的对象,在反序列化时,JVM创建对象并不会调用任何构造函数

前三种方式都会调用类的构造器,后两者则不会调用构造函数。

创建对象的步骤

  1. 判断对象对应的类是否加载、链接、初始化。
  2. 为对象分配内存
    如果内存规整,则可以采用碰撞指针的方式分配内存。
    如果内存不规整,则jvm会维护一个内存块空闲列表,根据空闲列表进行分配。
  3. 处理并发安全问题
    每个线程预先分配一块TLAB
    采用CAS和失败重试保证更新的原子性
  4. 初始化分配的空间
    为属性设置初始值
  5. 设置对象的对象头
  6. 执行init方法进行初始化

对象在内存中的存储

HotSpot虚拟机中,对象在内存中存储的布局可以分为三块区域:对象头(Header)、实例数据(Instance Data)和对齐填充(Padding)。

对象头

在maven项目中的pom文件引入查看对象布局的依赖

 <dependency>
            <groupId>org.openjdk.jol</groupId>
            <artifactId>jol-core</artifactId>
            <version>0.9</version>
        </dependency>
public static void main(String[] args) throws CloneNotSupportedException {
    Phone phone = new Phone("mi",1999);
    System.out.println(ClassLayout.
       parseInstance(phone).toPrintable());

}

Snipaste_2022-10-28_15-38-57.jpg

  • OFFSET:偏移地址,单位字节;
  • SIZE:占用的内存大小,单位为字节;
  • TYPE DESCRIPTION:类型描述,其中object header为对象头;
  • VALUE:对应内存中当前存储的值;

HotSpot虚拟机的对象头包括两部分信息,第一部分用于存储对象自身的运行时数据, 如哈希码(HashCode)、GC分代年龄、锁状态标志、线程持有的锁、偏向线程ID、偏向时间戳等等,这部分数据的长度在32位和64位的虚拟机(暂不考虑开启压缩指针的场景)中分别为32个和64个Bits,官方称它为“Mark Word”。

实例数据

它是对象真正存储的有效信息,包括程序代码中定义的各种类型的字段(包括从父类继承下来的和本身拥有的字段)

规则

  • 相同宽度的字段总是被分配在一起
  • 父类中定交的变量会出现在子类之前
  • 如果CompactFields参数为true(默认为true):子类的窄变量可能播入到父类变量的空隙